@cosmoops/connect-sdk 0.6.0 → 0.9.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.
Files changed (138) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/README.md +273 -91
  3. package/dist/cjs/cli/doctor.js +3 -3
  4. package/dist/cjs/cli/init.js +3 -3
  5. package/dist/cjs/client/components/access-gate.js +42 -7
  6. package/dist/cjs/client/components/account-sessions.js +3 -3
  7. package/dist/cjs/client/components/ai-format.js +135 -0
  8. package/dist/cjs/client/components/ai-widgets.js +232 -0
  9. package/dist/cjs/client/components/app-shell.js +9 -8
  10. package/dist/cjs/client/components/apps-grid.js +3 -3
  11. package/dist/cjs/client/components/ask-ai.js +334 -0
  12. package/dist/cjs/client/components/feature-gate.js +2 -2
  13. package/dist/cjs/client/components/integrations-modal.js +1 -1
  14. package/dist/cjs/client/components/logo.js +3 -3
  15. package/dist/cjs/client/components/nav.js +1 -1
  16. package/dist/cjs/client/components/plan-pricing.js +1 -1
  17. package/dist/cjs/client/components/plans.js +6 -6
  18. package/dist/cjs/client/components/readiness.js +3 -3
  19. package/dist/cjs/client/components/side-nav.js +1 -1
  20. package/dist/cjs/client/components/subscribe-dialog.js +2 -2
  21. package/dist/cjs/client/components/subscription.js +1 -1
  22. package/dist/cjs/client/components/usage-meter.js +1 -1
  23. package/dist/cjs/client/components/user-menu.js +31 -8
  24. package/dist/cjs/client/components/workspace-type-gate.js +48 -0
  25. package/dist/cjs/client/index.js +32 -4
  26. package/dist/cjs/client/mark.js +1 -1
  27. package/dist/cjs/client/provider.js +50 -7
  28. package/dist/cjs/client/use-checkout.js +3 -3
  29. package/dist/cjs/errors.js +4 -4
  30. package/dist/cjs/helpers.js +1 -1
  31. package/dist/cjs/index.js +1 -1
  32. package/dist/cjs/next/index.js +2 -2
  33. package/dist/cjs/server/client.js +15 -15
  34. package/dist/cjs/server/handler.js +56 -13
  35. package/dist/cjs/server/http.js +47 -25
  36. package/dist/cjs/server/resources/access.js +3 -3
  37. package/dist/cjs/server/resources/ai.js +1 -1
  38. package/dist/cjs/server/resources/analytics.js +2 -2
  39. package/dist/cjs/server/resources/apps.js +4 -4
  40. package/dist/cjs/server/resources/billing.js +5 -5
  41. package/dist/cjs/server/resources/drive.js +4 -4
  42. package/dist/cjs/server/resources/entitlements.js +48 -7
  43. package/dist/cjs/server/resources/identity.js +6 -4
  44. package/dist/cjs/server/resources/integrations.js +13 -13
  45. package/dist/cjs/server/resources/logs.js +3 -3
  46. package/dist/cjs/server/resources/sessions.js +6 -6
  47. package/dist/cjs/server/webhooks.js +4 -4
  48. package/dist/cjs/types.js +1 -1
  49. package/dist/esm/cli/doctor.js +3 -3
  50. package/dist/esm/cli/init.js +3 -3
  51. package/dist/esm/client/components/access-gate.js +43 -8
  52. package/dist/esm/client/components/account-sessions.js +3 -3
  53. package/dist/esm/client/components/ai-format.js +129 -0
  54. package/dist/esm/client/components/ai-widgets.js +223 -0
  55. package/dist/esm/client/components/app-shell.js +8 -7
  56. package/dist/esm/client/components/apps-grid.js +3 -3
  57. package/dist/esm/client/components/ask-ai.js +331 -0
  58. package/dist/esm/client/components/feature-gate.js +2 -2
  59. package/dist/esm/client/components/integrations-modal.js +1 -1
  60. package/dist/esm/client/components/logo.js +3 -3
  61. package/dist/esm/client/components/nav.js +1 -1
  62. package/dist/esm/client/components/plan-pricing.js +1 -1
  63. package/dist/esm/client/components/plans.js +6 -6
  64. package/dist/esm/client/components/readiness.js +3 -3
  65. package/dist/esm/client/components/side-nav.js +1 -1
  66. package/dist/esm/client/components/subscribe-dialog.js +2 -2
  67. package/dist/esm/client/components/subscription.js +1 -1
  68. package/dist/esm/client/components/usage-meter.js +1 -1
  69. package/dist/esm/client/components/user-menu.js +31 -8
  70. package/dist/esm/client/components/workspace-type-gate.js +45 -0
  71. package/dist/esm/client/index.js +14 -3
  72. package/dist/esm/client/mark.js +1 -1
  73. package/dist/esm/client/provider.js +48 -7
  74. package/dist/esm/client/use-checkout.js +3 -3
  75. package/dist/esm/errors.js +4 -4
  76. package/dist/esm/helpers.js +1 -1
  77. package/dist/esm/index.js +1 -1
  78. package/dist/esm/next/index.js +2 -2
  79. package/dist/esm/server/client.js +15 -15
  80. package/dist/esm/server/handler.js +56 -13
  81. package/dist/esm/server/http.js +47 -25
  82. package/dist/esm/server/resources/access.js +3 -3
  83. package/dist/esm/server/resources/ai.js +1 -1
  84. package/dist/esm/server/resources/analytics.js +2 -2
  85. package/dist/esm/server/resources/apps.js +4 -4
  86. package/dist/esm/server/resources/billing.js +5 -5
  87. package/dist/esm/server/resources/drive.js +4 -4
  88. package/dist/esm/server/resources/entitlements.js +48 -7
  89. package/dist/esm/server/resources/identity.js +6 -4
  90. package/dist/esm/server/resources/integrations.js +13 -13
  91. package/dist/esm/server/resources/logs.js +3 -3
  92. package/dist/esm/server/resources/sessions.js +6 -6
  93. package/dist/esm/server/webhooks.js +4 -4
  94. package/dist/esm/types.js +1 -1
  95. package/dist/theme.css +87 -0
  96. package/dist/types/client/components/access-gate.d.ts +18 -4
  97. package/dist/types/client/components/account-sessions.d.ts +1 -1
  98. package/dist/types/client/components/ai-format.d.ts +24 -0
  99. package/dist/types/client/components/ai-widgets.d.ts +42 -0
  100. package/dist/types/client/components/app-shell.d.ts +25 -9
  101. package/dist/types/client/components/apps-grid.d.ts +1 -1
  102. package/dist/types/client/components/ask-ai.d.ts +73 -0
  103. package/dist/types/client/components/feature-gate.d.ts +1 -1
  104. package/dist/types/client/components/integrations-modal.d.ts +1 -1
  105. package/dist/types/client/components/logo.d.ts +5 -5
  106. package/dist/types/client/components/nav.d.ts +3 -3
  107. package/dist/types/client/components/plan-pricing.d.ts +2 -2
  108. package/dist/types/client/components/readiness.d.ts +2 -2
  109. package/dist/types/client/components/side-nav.d.ts +1 -1
  110. package/dist/types/client/components/subscribe-dialog.d.ts +3 -3
  111. package/dist/types/client/components/usage-meter.d.ts +1 -1
  112. package/dist/types/client/components/user-menu.d.ts +4 -3
  113. package/dist/types/client/components/workspace-type-gate.d.ts +48 -0
  114. package/dist/types/client/index.d.ts +11 -4
  115. package/dist/types/client/mark.d.ts +1 -1
  116. package/dist/types/client/provider.d.ts +52 -9
  117. package/dist/types/client/use-checkout.d.ts +3 -3
  118. package/dist/types/errors.d.ts +4 -4
  119. package/dist/types/helpers.d.ts +1 -1
  120. package/dist/types/index.d.ts +1 -1
  121. package/dist/types/next/index.d.ts +1 -1
  122. package/dist/types/server/client.d.ts +13 -13
  123. package/dist/types/server/handler.d.ts +2 -2
  124. package/dist/types/server/http.d.ts +6 -1
  125. package/dist/types/server/resources/access.d.ts +3 -3
  126. package/dist/types/server/resources/ai.d.ts +2 -2
  127. package/dist/types/server/resources/analytics.d.ts +2 -2
  128. package/dist/types/server/resources/apps.d.ts +4 -4
  129. package/dist/types/server/resources/billing.d.ts +4 -4
  130. package/dist/types/server/resources/drive.d.ts +4 -4
  131. package/dist/types/server/resources/entitlements.d.ts +37 -5
  132. package/dist/types/server/resources/identity.d.ts +2 -2
  133. package/dist/types/server/resources/integrations.d.ts +9 -9
  134. package/dist/types/server/resources/logs.d.ts +3 -3
  135. package/dist/types/server/resources/sessions.d.ts +5 -5
  136. package/dist/types/server/webhooks.d.ts +4 -4
  137. package/dist/types/types.d.ts +235 -61
  138. package/package.json +8 -2
@@ -40,7 +40,7 @@ function parseRetryAfterMs(response) {
40
40
  return Number.isFinite(seconds) ? Math.max(0, seconds) * 1000 : null;
41
41
  }
42
42
  /**
43
- * Both error dialects, one shape. `/api/v1/service/*` answers
43
+ * Both error dialects, one shape. `/api/v1/application/*` answers
44
44
  * `{ error: { code, message } }`; the `/sso/*` endpoints answer OAuth2's
45
45
  * `{ error, error_description }`.
46
46
  */
@@ -124,33 +124,55 @@ export async function request(options, spec) {
124
124
  // request for minutes.
125
125
  await sleep(Math.min(lastError?.retryAfterMs ?? 250 * 2 ** (attempt - 1), 8_000));
126
126
  }
127
- const timeout = AbortSignal.timeout(options.timeoutMs);
128
- const signal = spec.signal ? AbortSignal.any([spec.signal, timeout]) : timeout;
129
- let response;
127
+ // A cancellable timer rather than `AbortSignal.timeout`, because the
128
+ // timeout has to be *stopped* once this attempt is done reading - and a
129
+ // timeout signal cannot be cleared.
130
+ //
131
+ // That distinction is the whole point for a streaming endpoint. `fetch`
132
+ // resolves the moment the headers land, but its signal keeps governing
133
+ // the body: an `AbortSignal.timeout(20_000)` handed to `ai.chat` tears the
134
+ // SSE stream down mid-answer, twenty seconds in, on every conversation
135
+ // long enough to matter. Clearing the timer in `finally` bounds the
136
+ // request the same way it always did (a `raw` caller still gets its
137
+ // timeout on *reaching* Connect, and a JSON caller still gets it across
138
+ // the body read, which happens inside the `try`) while leaving a stream
139
+ // that has actually started to run as long as it needs. A caller that
140
+ // wants to bound the stream itself passes `spec.signal`.
141
+ const timeoutController = new AbortController();
142
+ const timer = setTimeout(() => timeoutController.abort(), options.timeoutMs);
143
+ const signal = spec.signal
144
+ ? AbortSignal.any([spec.signal, timeoutController.signal])
145
+ : timeoutController.signal;
130
146
  try {
131
- response = await options.fetch(url.toString(), { method: spec.method, headers, body, signal });
147
+ let response;
148
+ try {
149
+ response = await options.fetch(url.toString(), { method: spec.method, headers, body, signal });
150
+ }
151
+ catch (cause) {
152
+ // A caller-supplied abort is a decision, not a fault - never retried.
153
+ if (spec.signal?.aborted)
154
+ throw cause;
155
+ lastError = new ConnectError({
156
+ status: 0,
157
+ code: "network_error",
158
+ message: `${endpoint} could not reach Connect`,
159
+ endpoint,
160
+ cause,
161
+ });
162
+ continue;
163
+ }
164
+ if (response.ok) {
165
+ if (spec.raw)
166
+ return response;
167
+ return (await readBody(response));
168
+ }
169
+ lastError = toConnectError(response, await readBody(response), endpoint);
170
+ if (!RETRYABLE_STATUS.has(response.status))
171
+ throw lastError;
132
172
  }
133
- catch (cause) {
134
- // A caller-supplied abort is a decision, not a fault - never retried.
135
- if (spec.signal?.aborted)
136
- throw cause;
137
- lastError = new ConnectError({
138
- status: 0,
139
- code: "network_error",
140
- message: `${endpoint} could not reach Connect`,
141
- endpoint,
142
- cause,
143
- });
144
- continue;
173
+ finally {
174
+ clearTimeout(timer);
145
175
  }
146
- if (response.ok) {
147
- if (spec.raw)
148
- return response;
149
- return (await readBody(response));
150
- }
151
- lastError = toConnectError(response, await readBody(response), endpoint);
152
- if (!RETRYABLE_STATUS.has(response.status))
153
- throw lastError;
154
176
  }
155
177
  throw lastError;
156
178
  }
@@ -1,5 +1,5 @@
1
1
  import { pathSegment, request } from "../http.js";
2
- /** What a signed-in member may do on your service, and the role catalog behind it. */
2
+ /** What a signed-in member may do on your application, and the role catalog behind it. */
3
3
  export class AccessResource {
4
4
  transport;
5
5
  constructor(transport) {
@@ -14,10 +14,10 @@ export class AccessResource {
14
14
  return request(this.transport, {
15
15
  method: "GET",
16
16
  path: `/rbac/users/${pathSegment(userId, "userId")}/roles`,
17
- query: { organizationId: input.organizationId },
17
+ query: { workspaceId: input.workspaceId },
18
18
  });
19
19
  }
20
- /** Your service's own permission catalog and role definitions - the definitions `getUserRoles` resolves against. */
20
+ /** Your application's own permission catalog and role definitions - the definitions `getUserRoles` resolves against. */
21
21
  listRoles() {
22
22
  return request(this.transport, { method: "GET", path: "/roles" });
23
23
  }
@@ -1,5 +1,5 @@
1
1
  import { request } from "../http.js";
2
- /** RAG chat and structured extraction against an organization's data. */
2
+ /** RAG chat and structured extraction against a workspace's data. */
3
3
  export class AiResource {
4
4
  transport;
5
5
  constructor(transport) {
@@ -1,9 +1,9 @@
1
1
  import { request } from "../http.js";
2
2
  /**
3
- * Your service's own analytics: emit product events, and read back the daily
3
+ * Your application's own analytics: emit product events, and read back the daily
4
4
  * numbers Connect aggregates from them.
5
5
  *
6
- * There is no way to name a service in either call. The scope is always the one
6
+ * There is no way to name an application in either call. The scope is always the one
7
7
  * your API key belongs to - that is the endpoint's security model, not an
8
8
  * omission.
9
9
  */
@@ -1,5 +1,5 @@
1
1
  import { request } from "../http.js";
2
- /** The services an organization can open - the data behind a launcher grid. */
2
+ /** The applications a workspace can open - the data behind a launcher grid. */
3
3
  export class AppsResource {
4
4
  transport;
5
5
  constructor(transport) {
@@ -7,13 +7,13 @@ export class AppsResource {
7
7
  }
8
8
  /**
9
9
  * Launchable apps only, so a grid tile is never a dead end. The calling
10
- * service is included and flagged `isCurrent` rather than filtered out.
10
+ * application is included and flagged `isCurrent` rather than filtered out.
11
11
  */
12
- async list(organizationId) {
12
+ async list(workspaceId) {
13
13
  const result = await request(this.transport, {
14
14
  method: "GET",
15
15
  path: "/apps",
16
- query: { organizationId },
16
+ query: { workspaceId },
17
17
  });
18
18
  // `?? []` for the same reason `billing.listPlans` has it: this is a
19
19
  // collection every caller immediately maps or measures, and a body without
@@ -7,7 +7,7 @@ export class BillingResource {
7
7
  this.transport = transport;
8
8
  }
9
9
  /**
10
- * Your service's active plans - render live pricing instead of hardcoding it.
10
+ * Your application's active plans - render live pricing instead of hardcoding it.
11
11
  *
12
12
  * `features` is normalised on the way out: Connect used to send bare keys
13
13
  * and now sends `{ key, name, description }`, and this SDK is versioned
@@ -25,7 +25,7 @@ export class BillingResource {
25
25
  };
26
26
  }
27
27
  /**
28
- * What is left of every consumable add-on on an organization's
28
+ * What is left of every consumable add-on on a workspace's
29
29
  * subscription - the read half of `consumeAddon`.
30
30
  *
31
31
  * Needs a key scoped to `addons:read`, which is separate from
@@ -35,10 +35,10 @@ export class BillingResource {
35
35
  * Returns an empty `addons` array rather than throwing when there is
36
36
  * nothing to meter, so a caller can render it unconditionally.
37
37
  */
38
- async getAddonUsage(organizationId) {
38
+ async getAddonUsage(workspaceId) {
39
39
  const response = await request(this.transport, {
40
40
  method: "GET",
41
- path: `/addons/organizations/${pathSegment(organizationId, "organizationId")}`,
41
+ path: `/addons/workspaces/${pathSegment(workspaceId, "workspaceId")}`,
42
42
  });
43
43
  // Normalised, not passed through: the promise above resolves for any 2xx,
44
44
  // and `ConnectUsageMeter` reads `addons` straight off it. A body without
@@ -49,7 +49,7 @@ export class BillingResource {
49
49
  }
50
50
  /**
51
51
  * Debit a consumable add-on's per-cycle balance on behalf of an
52
- * organization's users. Call it at whatever cadence your usage happens.
52
+ * workspace's users. Call it at whatever cadence your usage happens.
53
53
  *
54
54
  * An `Idempotency-Key` is always sent, generated per call when you do not
55
55
  * supply one: a balance cannot be un-spent, so a retried consume without a
@@ -1,5 +1,5 @@
1
1
  import { pathSegment, request } from "../http.js";
2
- /** Files in an organization's Drive - upload, and signed URLs to read back. */
2
+ /** Files in a workspace's Drive - upload, and signed URLs to read back. */
3
3
  export class DriveResource {
4
4
  transport;
5
5
  constructor(transport) {
@@ -9,7 +9,7 @@ export class DriveResource {
9
9
  const form = new FormData();
10
10
  const fileName = input.fileName ?? input.file.name ?? "upload";
11
11
  form.set("file", input.file, fileName);
12
- form.set("organizationId", input.organizationId);
12
+ form.set("workspaceId", input.workspaceId);
13
13
  if (input.parentId)
14
14
  form.set("parentId", input.parentId);
15
15
  return request(this.transport, { method: "POST", path: "/drive/files", multipart: form });
@@ -19,7 +19,7 @@ export class DriveResource {
19
19
  const result = await request(this.transport, {
20
20
  method: "GET",
21
21
  path: `/drive/files/${pathSegment(fileId, "fileId")}/url`,
22
- query: { organizationId: input.organizationId },
22
+ query: { workspaceId: input.workspaceId },
23
23
  });
24
24
  return result.url;
25
25
  }
@@ -28,7 +28,7 @@ export class DriveResource {
28
28
  const result = await request(this.transport, {
29
29
  method: "GET",
30
30
  path: `/drive/files/${pathSegment(fileId, "fileId")}/preview-url`,
31
- query: { organizationId: input.organizationId, size: input.size },
31
+ query: { workspaceId: input.workspaceId, size: input.size },
32
32
  });
33
33
  return result.url;
34
34
  }
@@ -9,7 +9,7 @@ export class EntitlementsResource {
9
9
  this.transport = transport;
10
10
  }
11
11
  /**
12
- * Every gate Connect owns between an organization and your service -
12
+ * Every gate Connect owns between a workspace and your application -
13
13
  * subscription, KYC/KYB, a chargeable payment method, seat headroom -
14
14
  * resolved in one call.
15
15
  *
@@ -17,17 +17,17 @@ export class EntitlementsResource {
17
17
  * Connect screen that fixes it, so you can route them somewhere useful
18
18
  * instead of refusing them with nowhere to go.
19
19
  */
20
- getReadiness(organizationId) {
20
+ getReadiness(workspaceId) {
21
21
  return request(this.transport, {
22
22
  method: "GET",
23
- path: `/onboarding/organizations/${pathSegment(organizationId, "organizationId")}`,
23
+ path: `/onboarding/workspaces/${pathSegment(workspaceId, "workspaceId")}`,
24
24
  });
25
25
  }
26
26
  /** The live plan and subscription, including `plan.features`. */
27
- getSubscription(organizationId) {
27
+ getSubscription(workspaceId) {
28
28
  return request(this.transport, {
29
29
  method: "GET",
30
- path: `/subscriptions/organizations/${pathSegment(organizationId, "organizationId")}`,
30
+ path: `/subscriptions/workspaces/${pathSegment(workspaceId, "workspaceId")}`,
31
31
  });
32
32
  }
33
33
  /**
@@ -38,8 +38,49 @@ export class EntitlementsResource {
38
38
  * all: "not entitled" is the same answer either way, and a feature gate
39
39
  * should not need a try/catch around it.
40
40
  */
41
- async hasFeature(organizationId, feature) {
42
- const result = await this.getSubscription(organizationId);
41
+ async hasFeature(workspaceId, feature) {
42
+ const result = await this.getSubscription(workspaceId);
43
43
  return result.hasActiveSubscription && (result.plan?.features.includes(feature) ?? false);
44
44
  }
45
+ /**
46
+ * One page of the workspaces subscribed to your application, newest first.
47
+ *
48
+ * The inverse of `getSubscription`, which asks about one workspace you
49
+ * already know. This asks who your subscribers *are* - what a partner
50
+ * dashboard, an MRR total or a churn report is built on.
51
+ *
52
+ * Rate-limited far more tightly than the per-workspace read (10/min per
53
+ * key, against 60): it walks your whole subscriber base and is a reporting
54
+ * call, not a per-request gate. Use `getSubscription` in a hot path.
55
+ *
56
+ * Your application is resolved from the API key, so there is no parameter
57
+ * for it and no way to ask about anyone else's.
58
+ */
59
+ listSubscribers(options = {}) {
60
+ return request(this.transport, {
61
+ method: "GET",
62
+ path: "/subscriptions",
63
+ query: { status: options.status, before: options.before, limit: options.limit },
64
+ });
65
+ }
66
+ /**
67
+ * Every subscriber, paged through for you.
68
+ *
69
+ * An async generator rather than an array: a large subscriber base is
70
+ * exactly where an accidental full materialisation hurts, and `for await`
71
+ * reads the same as a loop over one page.
72
+ */
73
+ async *iterateSubscribers(options = {}) {
74
+ let before;
75
+ do {
76
+ const page = await this.listSubscribers({
77
+ status: options.status,
78
+ limit: options.pageSize,
79
+ before,
80
+ });
81
+ for (const item of page.items)
82
+ yield item;
83
+ before = page.nextBefore ?? undefined;
84
+ } while (before);
85
+ }
45
86
  }
@@ -66,7 +66,8 @@ export class IdentityResource {
66
66
  sessionTokenExpiresIn: wire.session_token_expires_in,
67
67
  user: wire.user,
68
68
  subscription: wire.subscription,
69
- serviceRole: wire.serviceRole,
69
+ applicationRole: wire.applicationRole,
70
+ audience: wire.audience,
70
71
  };
71
72
  }
72
73
  /**
@@ -136,7 +137,8 @@ export class IdentityResource {
136
137
  return {
137
138
  user: wire.user,
138
139
  subscription: wire.subscription,
139
- serviceRole: wire.serviceRole,
140
+ applicationRole: wire.applicationRole,
141
+ audience: wire.audience,
140
142
  sessionExpiresAt: wire.session_expires_at,
141
143
  };
142
144
  }
@@ -145,7 +147,7 @@ export class IdentityResource {
145
147
  * rest of its ~15-day life. Discard your own copy of `sessionToken` too;
146
148
  * this does not do that for you.
147
149
  *
148
- * `scope` defaults to `"device"`: this sign-in, every other service signed
150
+ * `scope` defaults to `"device"`: this sign-in, every other application signed
149
151
  * into from the same browser, and Connect's own session on that one device.
150
152
  * `"all"` ends every session on every device - an explicit "sign out
151
153
  * everywhere" action only.
@@ -177,7 +179,7 @@ export class IdentityResource {
177
179
  throw new ConnectError({
178
180
  status: 0,
179
181
  code: "missing_client_id",
180
- message: "identity.* needs `clientId`. Pass it to `new ConnectClient({ clientId, apiKey })` - it is shown in this service's Integration tab, under Connect client.",
182
+ message: "identity.* needs `clientId`. Pass it to `new ConnectClient({ clientId, apiKey })` - it is shown in this application's Integration tab, under Connect client.",
181
183
  endpoint: "identity",
182
184
  });
183
185
  }
@@ -1,8 +1,8 @@
1
1
  import { request } from "../http.js";
2
2
  /**
3
3
  * Composio-hosted third-party connections (Google Calendar, Slack,
4
- * Twitter/X, ...) an organization has connected. Connect proxies every call
5
- * here - your service never sees or holds the underlying OAuth token, which
4
+ * Twitter/X, ...) a workspace has connected. Connect proxies every call
5
+ * here - your application never sees or holds the underlying OAuth token, which
6
6
  * stays with Composio.
7
7
  */
8
8
  export class IntegrationsResource {
@@ -10,12 +10,12 @@ export class IntegrationsResource {
10
10
  constructor(transport) {
11
11
  this.transport = transport;
12
12
  }
13
- /** Enabled toolkits and this organization's connection status for each. */
14
- async list(organizationId) {
13
+ /** Enabled toolkits and this workspace's connection status for each. */
14
+ async list(workspaceId) {
15
15
  const result = await request(this.transport, {
16
16
  method: "GET",
17
17
  path: "/integrations",
18
- query: { organizationId },
18
+ query: { workspaceId },
19
19
  });
20
20
  // `?? []` for the same reason `AppsResource.list` has it - a collection
21
21
  // every caller immediately maps or measures.
@@ -32,31 +32,31 @@ export class IntegrationsResource {
32
32
  * itself. Reconnecting a disconnected/failed toolkit calls this same
33
33
  * method - there is no separate reconnect.
34
34
  *
35
- * Meant to be called on behalf of a signed-in org admin (this is what the
35
+ * Meant to be called on behalf of a signed-in workspace admin (this is what the
36
36
  * bridge's fixed `POST /integrations/connect` route does), not as a bare
37
37
  * background action with no admin present to complete the OAuth screen.
38
38
  */
39
- async initiateConnect(organizationId, toolkitSlug, returnUrl) {
39
+ async initiateConnect(workspaceId, toolkitSlug, returnUrl) {
40
40
  return request(this.transport, {
41
41
  method: "POST",
42
42
  path: "/integrations/connect",
43
- json: { organizationId, toolkitSlug, returnUrl },
43
+ json: { workspaceId, toolkitSlug, returnUrl },
44
44
  });
45
45
  }
46
46
  /** Idempotent - disconnecting a toolkit that was never connected, or is already disconnected, is a success. */
47
- async disconnect(organizationId, toolkitSlug) {
47
+ async disconnect(workspaceId, toolkitSlug) {
48
48
  return request(this.transport, {
49
49
  method: "POST",
50
50
  path: "/integrations/disconnect",
51
- json: { organizationId, toolkitSlug },
51
+ json: { workspaceId, toolkitSlug },
52
52
  });
53
53
  }
54
- /** Calls a Composio action against this organization's connected account for the named toolkit. */
55
- async execute(organizationId, input) {
54
+ /** Calls a Composio action against this workspace's connected account for the named toolkit. */
55
+ async execute(workspaceId, input) {
56
56
  return request(this.transport, {
57
57
  method: "POST",
58
58
  path: "/integrations/actions/execute",
59
- json: { organizationId, ...input },
59
+ json: { workspaceId, ...input },
60
60
  });
61
61
  }
62
62
  }
@@ -1,5 +1,5 @@
1
1
  import { request } from "../http.js";
2
- /** Push feature events into the Console's audit log, filterable to your service. */
2
+ /** Push feature events into the Console's audit log, filterable to your application. */
3
3
  export class LogsResource {
4
4
  transport;
5
5
  constructor(transport) {
@@ -11,9 +11,9 @@ export class LogsResource {
11
11
  * reported through the return value.
12
12
  *
13
13
  * Pass `sessionToken` to attribute the event to the end user it belongs to.
14
- * Both scopes on the stored entry come from something verified - the service
14
+ * Both scopes on the stored entry come from something verified - the application
15
15
  * from your API key, the user from that token, which Connect checks was
16
- * minted for your service before attributing anything. `attributed` in the
16
+ * minted for your application before attributing anything. `attributed` in the
17
17
  * result says whether it resolved; a `false` there still means the line was
18
18
  * stored, just without a user on it.
19
19
  */
@@ -5,11 +5,11 @@ import { request } from "../http.js";
5
5
  * timestamps - the same list Connect's own account settings page shows the
6
6
  * user themselves.
7
7
  *
8
- * **Account-wide, not scoped to this service.** A Connect session is one
9
- * browser's login to Connect, and it fans out to every partner service's own
10
- * `sessionToken` - Connect has no per-service session concept to filter by.
8
+ * **Account-wide, not scoped to this application.** A Connect session is one
9
+ * browser's login to Connect, and it fans out to every partner application's own
10
+ * `sessionToken` - Connect has no per-application session concept to filter by.
11
11
  * `list()` returns every session on the account, including ones that
12
- * originated from a completely different, unrelated service. Requires the
12
+ * originated from a completely different, unrelated application. Requires the
13
13
  * `sessions:manage` scope on your API key - unlike `identity`'s base `sso`
14
14
  * capability, this is never granted to a zero-scope legacy key by default.
15
15
  *
@@ -35,7 +35,7 @@ export class SessionsResource {
35
35
  }
36
36
  /**
37
37
  * Ends one specific session by id - not necessarily the one `sessionToken`
38
- * itself was minted from. Cascades to every partner-service sign-in that
38
+ * itself was minted from. Cascades to every partner-application sign-in that
39
39
  * originated from that same browser, same as Connect's own "sign out this
40
40
  * device".
41
41
  */
@@ -69,7 +69,7 @@ export class SessionsResource {
69
69
  throw new ConnectError({
70
70
  status: 0,
71
71
  code: "missing_client_id",
72
- message: "sessions.* needs `clientId`. Pass it to `new ConnectClient({ clientId, apiKey })` - it is shown in this service's Integration tab, under Connect client.",
72
+ message: "sessions.* needs `clientId`. Pass it to `new ConnectClient({ clientId, apiKey })` - it is shown in this application's Integration tab, under Connect client.",
73
73
  endpoint: "sessions",
74
74
  });
75
75
  }
@@ -54,10 +54,10 @@ export function verifyConnectWebhook(rawBody, signatureHeader, secret, now = new
54
54
  * export const POST = createConnectWebhookHandler({
55
55
  * secret: process.env.CONNECT_WEBHOOK_SECRET!,
56
56
  * on: {
57
- * "organization.deletion_scheduled": ({ organizationId, purgeAfter }) =>
58
- * freeze(organizationId, purgeAfter),
59
- * "organization.deletion_cancelled": ({ organizationId }) => unfreeze(organizationId),
60
- * "organization.deleted": ({ organizationId }) => purge(organizationId),
57
+ * "workspace.deletion_scheduled": ({ workspaceId, purgeAfter }) =>
58
+ * freeze(workspaceId, purgeAfter),
59
+ * "workspace.deletion_cancelled": ({ workspaceId }) => unfreeze(workspaceId),
60
+ * "workspace.deleted": ({ workspaceId }) => purge(workspaceId),
61
61
  * },
62
62
  * })
63
63
  * ```
package/dist/esm/types.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Wire types for the `/api/v1/service/*` surface - the service-partner API,
2
+ * Wire types for the `/api/v1/application/*` surface - the application-partner API,
3
3
  * and the only thing this SDK covers.
4
4
  *
5
5
  * Hand-written rather than generated so the published package carries no
package/dist/theme.css CHANGED
@@ -110,3 +110,90 @@ body {
110
110
  background: var(--background);
111
111
  color: var(--foreground);
112
112
  }
113
+
114
+ /* ---------------------------------------------------------------------------
115
+ * Ask AI: Streamdown token compatibility.
116
+ *
117
+ * `ConnectAskAi` renders markdown with Streamdown, which paints using
118
+ * shadcn/ui's token names. HeroUI defines a different set - four happen to line
119
+ * up already (`--background`, `--foreground`, `--border`, `--radius`), and the
120
+ * rest are aliased here so an answer inherits the platform's own theme instead
121
+ * of rendering with missing backgrounds and borders.
122
+ *
123
+ * This lives in the shipped theme rather than in each partner's CSS because it
124
+ * is a fact about how these two libraries name colours, not a design choice
125
+ * anyone should have to rediscover.
126
+ *
127
+ * Additive - HeroUI uses none of these names - with exactly one real collision,
128
+ * handled below.
129
+ * ------------------------------------------------------------------------ */
130
+ :root,
131
+ .light,
132
+ .default,
133
+ [data-theme="light"],
134
+ [data-theme="default"],
135
+ .dark,
136
+ [data-theme="dark"] {
137
+ --card: var(--surface);
138
+ --card-foreground: var(--surface-foreground);
139
+ --input: var(--border);
140
+ --primary: var(--accent);
141
+ --primary-foreground: var(--accent-foreground);
142
+ }
143
+
144
+ /* ---------------------------------------------------------------------------
145
+ * The collision: `--muted` is a *surface* in shadcn (code-block fills, table
146
+ * header rows) and a *text colour* in HeroUI. Reading it as HeroUI's would
147
+ * paint every code block mid-grey and leave the text on it unreadable.
148
+ *
149
+ * So it is shadowed - but only inside the answer body, and only after the host
150
+ * element has captured HeroUI's value under shadcn's name for it. Splitting it
151
+ * across two elements is what makes this safe: `--muted-foreground` is computed
152
+ * on `.connect-ai-host`, where `--muted` still means HeroUI's text grey, and
153
+ * inherits that resolved colour downward. Both declarations on one element
154
+ * would resolve `var(--muted)` against the new value and lose the original.
155
+ * ------------------------------------------------------------------------ */
156
+ .connect-ai-host {
157
+ --muted-foreground: var(--muted);
158
+ }
159
+
160
+ .connect-ai-host .connect-ai-body {
161
+ --muted: var(--surface-secondary);
162
+ }
163
+
164
+ /* ---------------------------------------------------------------------------
165
+ * The Ask AI "thinking" shimmer - a highlight sweeping through the label while
166
+ * the model works, rather than a spinner.
167
+ *
168
+ * A gradient clipped to the glyphs, so the motion is *in* the word instead of
169
+ * beside it. This is the one state where the panel has nothing at all to show:
170
+ * retrieval and the model's first thought take a second or two, and a static
171
+ * "Thinking" reads as frozen.
172
+ * ------------------------------------------------------------------------ */
173
+ .connect-ai-shimmer {
174
+ background: linear-gradient(100deg, var(--muted) 32%, var(--accent) 48%, var(--muted) 64%);
175
+ background-size: 220% 100%;
176
+ -webkit-background-clip: text;
177
+ background-clip: text;
178
+ color: transparent;
179
+ animation: connect-ai-shimmer 1.7s linear infinite;
180
+ }
181
+
182
+ @keyframes connect-ai-shimmer {
183
+ from {
184
+ background-position: 180% 0;
185
+ }
186
+ to {
187
+ background-position: -40% 0;
188
+ }
189
+ }
190
+
191
+ /* Motion is the whole affordance here, so with it suppressed the label has to
192
+ go back to being a legible static word rather than a transparent one. */
193
+ @media (prefers-reduced-motion: reduce) {
194
+ .connect-ai-shimmer {
195
+ background: none;
196
+ color: var(--muted);
197
+ animation: none;
198
+ }
199
+ }
@@ -3,8 +3,14 @@ import type { ReadinessBlocker } from "../../types.js";
3
3
  export type ConnectAccessGateState = "loading"
4
4
  /** No signed-in user - not this component's concern, see the doc comment on `ConnectAccessGate`. */
5
5
  | "signed_out"
6
- /** `organization_suspended` - a platform admin took this workspace offline. Hard block, no dismiss. */
6
+ /** `workspace_suspended` - a platform admin took this workspace offline. Hard block, no dismiss. */
7
7
  | "suspended"
8
+ /**
9
+ * `audience_mismatch` - this application serves individual accounts and the
10
+ * workspace is a business, or the reverse. Hard block with nothing to
11
+ * offer: `Workspace.type` is fixed at sign-up.
12
+ */
13
+ | "audience_mismatch"
8
14
  /** `subscription_missing` - nothing to subscribe to yet. Hard block. */
9
15
  | "no_subscription"
10
16
  /** `onboarding_approval_pending` - waiting on a reviewer. Hard block, no action to offer. */
@@ -33,9 +39,17 @@ export interface ConnectAccessGateResult {
33
39
  * `ConnectReadinessGate`/`ConnectReadinessBanner` already use. `ConnectAccessGate`
34
40
  * is a priority ordering over the same data, not a second opinion about it.
35
41
  *
36
- * Priority, first match wins: `suspended` > `no_subscription` >
37
- * `onboarding_pending` > any other blocking code (`blocked`) >
38
- * `payment_retry` (advisory - `children` still render) > `clear`.
42
+ * Priority, first match wins: `suspended` > `audience_mismatch` >
43
+ * `no_subscription` > `onboarding_pending` > any other blocking code
44
+ * (`blocked`) > `payment_retry` (advisory - `children` still render) >
45
+ * `clear`.
46
+ *
47
+ * `audience_mismatch` outranks `no_subscription` deliberately, and the
48
+ * ordering is the whole point of it: a workspace of the wrong account type
49
+ * carries BOTH blockers at once, and showing the subscription one first sent
50
+ * the customer to a plan picker whose subscribe call
51
+ * (`ActivateSubscriptionUseCase`) rejects them for the audience anyway. The
52
+ * account type is the reason; the missing subscription is a consequence.
39
53
  */
40
54
  export declare function useConnectAccessGate(): ConnectAccessGateResult;
41
55
  export interface ConnectAccessGateProps {
@@ -10,7 +10,7 @@ export interface ConnectAccountSessionsProps {
10
10
  }
11
11
  /**
12
12
  * Every device signed into this Connect account - not scoped to this
13
- * service, see `ConnectUserSession`'s doc comment. Owns the one
13
+ * application, see `ConnectUserSession`'s doc comment. Owns the one
14
14
  * `useConnectSessions()` call for this modal; every revoke action here
15
15
  * mutates through it and re-reads the list itself, since nothing else
16
16
  * invalidates it automatically - see `mutateResource`'s doc comment.