@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.
- package/CHANGELOG.md +165 -0
- package/README.md +273 -91
- package/dist/cjs/cli/doctor.js +3 -3
- package/dist/cjs/cli/init.js +3 -3
- package/dist/cjs/client/components/access-gate.js +42 -7
- package/dist/cjs/client/components/account-sessions.js +3 -3
- package/dist/cjs/client/components/ai-format.js +135 -0
- package/dist/cjs/client/components/ai-widgets.js +232 -0
- package/dist/cjs/client/components/app-shell.js +9 -8
- package/dist/cjs/client/components/apps-grid.js +3 -3
- package/dist/cjs/client/components/ask-ai.js +334 -0
- package/dist/cjs/client/components/feature-gate.js +2 -2
- package/dist/cjs/client/components/integrations-modal.js +1 -1
- package/dist/cjs/client/components/logo.js +3 -3
- package/dist/cjs/client/components/nav.js +1 -1
- package/dist/cjs/client/components/plan-pricing.js +1 -1
- package/dist/cjs/client/components/plans.js +6 -6
- package/dist/cjs/client/components/readiness.js +3 -3
- package/dist/cjs/client/components/side-nav.js +1 -1
- package/dist/cjs/client/components/subscribe-dialog.js +2 -2
- package/dist/cjs/client/components/subscription.js +1 -1
- package/dist/cjs/client/components/usage-meter.js +1 -1
- package/dist/cjs/client/components/user-menu.js +31 -8
- package/dist/cjs/client/components/workspace-type-gate.js +48 -0
- package/dist/cjs/client/index.js +32 -4
- package/dist/cjs/client/mark.js +1 -1
- package/dist/cjs/client/provider.js +50 -7
- package/dist/cjs/client/use-checkout.js +3 -3
- package/dist/cjs/errors.js +4 -4
- package/dist/cjs/helpers.js +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/next/index.js +2 -2
- package/dist/cjs/server/client.js +15 -15
- package/dist/cjs/server/handler.js +56 -13
- package/dist/cjs/server/http.js +47 -25
- package/dist/cjs/server/resources/access.js +3 -3
- package/dist/cjs/server/resources/ai.js +1 -1
- package/dist/cjs/server/resources/analytics.js +2 -2
- package/dist/cjs/server/resources/apps.js +4 -4
- package/dist/cjs/server/resources/billing.js +5 -5
- package/dist/cjs/server/resources/drive.js +4 -4
- package/dist/cjs/server/resources/entitlements.js +48 -7
- package/dist/cjs/server/resources/identity.js +6 -4
- package/dist/cjs/server/resources/integrations.js +13 -13
- package/dist/cjs/server/resources/logs.js +3 -3
- package/dist/cjs/server/resources/sessions.js +6 -6
- package/dist/cjs/server/webhooks.js +4 -4
- package/dist/cjs/types.js +1 -1
- package/dist/esm/cli/doctor.js +3 -3
- package/dist/esm/cli/init.js +3 -3
- package/dist/esm/client/components/access-gate.js +43 -8
- package/dist/esm/client/components/account-sessions.js +3 -3
- package/dist/esm/client/components/ai-format.js +129 -0
- package/dist/esm/client/components/ai-widgets.js +223 -0
- package/dist/esm/client/components/app-shell.js +8 -7
- package/dist/esm/client/components/apps-grid.js +3 -3
- package/dist/esm/client/components/ask-ai.js +331 -0
- package/dist/esm/client/components/feature-gate.js +2 -2
- package/dist/esm/client/components/integrations-modal.js +1 -1
- package/dist/esm/client/components/logo.js +3 -3
- package/dist/esm/client/components/nav.js +1 -1
- package/dist/esm/client/components/plan-pricing.js +1 -1
- package/dist/esm/client/components/plans.js +6 -6
- package/dist/esm/client/components/readiness.js +3 -3
- package/dist/esm/client/components/side-nav.js +1 -1
- package/dist/esm/client/components/subscribe-dialog.js +2 -2
- package/dist/esm/client/components/subscription.js +1 -1
- package/dist/esm/client/components/usage-meter.js +1 -1
- package/dist/esm/client/components/user-menu.js +31 -8
- package/dist/esm/client/components/workspace-type-gate.js +45 -0
- package/dist/esm/client/index.js +14 -3
- package/dist/esm/client/mark.js +1 -1
- package/dist/esm/client/provider.js +48 -7
- package/dist/esm/client/use-checkout.js +3 -3
- package/dist/esm/errors.js +4 -4
- package/dist/esm/helpers.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/next/index.js +2 -2
- package/dist/esm/server/client.js +15 -15
- package/dist/esm/server/handler.js +56 -13
- package/dist/esm/server/http.js +47 -25
- package/dist/esm/server/resources/access.js +3 -3
- package/dist/esm/server/resources/ai.js +1 -1
- package/dist/esm/server/resources/analytics.js +2 -2
- package/dist/esm/server/resources/apps.js +4 -4
- package/dist/esm/server/resources/billing.js +5 -5
- package/dist/esm/server/resources/drive.js +4 -4
- package/dist/esm/server/resources/entitlements.js +48 -7
- package/dist/esm/server/resources/identity.js +6 -4
- package/dist/esm/server/resources/integrations.js +13 -13
- package/dist/esm/server/resources/logs.js +3 -3
- package/dist/esm/server/resources/sessions.js +6 -6
- package/dist/esm/server/webhooks.js +4 -4
- package/dist/esm/types.js +1 -1
- package/dist/theme.css +87 -0
- package/dist/types/client/components/access-gate.d.ts +18 -4
- package/dist/types/client/components/account-sessions.d.ts +1 -1
- package/dist/types/client/components/ai-format.d.ts +24 -0
- package/dist/types/client/components/ai-widgets.d.ts +42 -0
- package/dist/types/client/components/app-shell.d.ts +25 -9
- package/dist/types/client/components/apps-grid.d.ts +1 -1
- package/dist/types/client/components/ask-ai.d.ts +73 -0
- package/dist/types/client/components/feature-gate.d.ts +1 -1
- package/dist/types/client/components/integrations-modal.d.ts +1 -1
- package/dist/types/client/components/logo.d.ts +5 -5
- package/dist/types/client/components/nav.d.ts +3 -3
- package/dist/types/client/components/plan-pricing.d.ts +2 -2
- package/dist/types/client/components/readiness.d.ts +2 -2
- package/dist/types/client/components/side-nav.d.ts +1 -1
- package/dist/types/client/components/subscribe-dialog.d.ts +3 -3
- package/dist/types/client/components/usage-meter.d.ts +1 -1
- package/dist/types/client/components/user-menu.d.ts +4 -3
- package/dist/types/client/components/workspace-type-gate.d.ts +48 -0
- package/dist/types/client/index.d.ts +11 -4
- package/dist/types/client/mark.d.ts +1 -1
- package/dist/types/client/provider.d.ts +52 -9
- package/dist/types/client/use-checkout.d.ts +3 -3
- package/dist/types/errors.d.ts +4 -4
- package/dist/types/helpers.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/next/index.d.ts +1 -1
- package/dist/types/server/client.d.ts +13 -13
- package/dist/types/server/handler.d.ts +2 -2
- package/dist/types/server/http.d.ts +6 -1
- package/dist/types/server/resources/access.d.ts +3 -3
- package/dist/types/server/resources/ai.d.ts +2 -2
- package/dist/types/server/resources/analytics.d.ts +2 -2
- package/dist/types/server/resources/apps.d.ts +4 -4
- package/dist/types/server/resources/billing.d.ts +4 -4
- package/dist/types/server/resources/drive.d.ts +4 -4
- package/dist/types/server/resources/entitlements.d.ts +37 -5
- package/dist/types/server/resources/identity.d.ts +2 -2
- package/dist/types/server/resources/integrations.d.ts +9 -9
- package/dist/types/server/resources/logs.d.ts +3 -3
- package/dist/types/server/resources/sessions.d.ts +5 -5
- package/dist/types/server/webhooks.d.ts +4 -4
- package/dist/types/types.d.ts +235 -61
- package/package.json +8 -2
|
@@ -94,9 +94,9 @@ function redirect(location, cookies = []) {
|
|
|
94
94
|
* export { handler as GET, handler as POST };
|
|
95
95
|
* ```
|
|
96
96
|
*
|
|
97
|
-
* Every route resolves the
|
|
97
|
+
* Every route resolves the workspace from the caller's *own* session, never
|
|
98
98
|
* from a query parameter. A browser must not be able to ask about an
|
|
99
|
-
*
|
|
99
|
+
* workspace it is not signed into, and a client-supplied `workspaceId`
|
|
100
100
|
* is exactly that hole.
|
|
101
101
|
*/
|
|
102
102
|
function createConnectHandler(options) {
|
|
@@ -163,7 +163,7 @@ function createConnectHandler(options) {
|
|
|
163
163
|
}
|
|
164
164
|
return token;
|
|
165
165
|
}
|
|
166
|
-
/** Signed-in identity plus the
|
|
166
|
+
/** Signed-in identity plus the workspace it belongs to - the first read every other route needs. */
|
|
167
167
|
async function whoami(request) {
|
|
168
168
|
return client.identity.getUser(await requireToken(request));
|
|
169
169
|
}
|
|
@@ -173,32 +173,75 @@ function createConnectHandler(options) {
|
|
|
173
173
|
// Signed out is a normal state for a page rendering a Login button, not
|
|
174
174
|
// an error - `user: null` rather than a 401 the client must catch.
|
|
175
175
|
if (!token)
|
|
176
|
-
return json({ user: null, subscription: null,
|
|
176
|
+
return json({ user: null, subscription: null, applicationRole: null });
|
|
177
177
|
return json(await client.identity.getUser(token));
|
|
178
178
|
},
|
|
179
179
|
"GET /entitlements": async (request) => {
|
|
180
180
|
const { user } = await whoami(request);
|
|
181
181
|
const [readiness, subscription] = await Promise.all([
|
|
182
|
-
client.entitlements.getReadiness(user.
|
|
183
|
-
client.entitlements.getSubscription(user.
|
|
182
|
+
client.entitlements.getReadiness(user.workspaceId),
|
|
183
|
+
client.entitlements.getSubscription(user.workspaceId),
|
|
184
184
|
]);
|
|
185
185
|
return json({ readiness, subscription });
|
|
186
186
|
},
|
|
187
187
|
"GET /plans": async () => json(await client.billing.listPlans()),
|
|
188
|
-
//
|
|
188
|
+
// Workspace comes from the caller's own session, same as every other
|
|
189
189
|
// route here - a browser must not be able to ask what another workspace
|
|
190
190
|
// has left.
|
|
191
191
|
"GET /addons": async (request) => {
|
|
192
192
|
const { user } = await whoami(request);
|
|
193
|
-
return json(await client.billing.getAddonUsage(user.
|
|
193
|
+
return json(await client.billing.getAddonUsage(user.workspaceId));
|
|
194
194
|
},
|
|
195
195
|
"GET /apps": async (request) => {
|
|
196
196
|
const { user } = await whoami(request);
|
|
197
|
-
return json({ apps: await client.apps.list(user.
|
|
197
|
+
return json({ apps: await client.apps.list(user.workspaceId) });
|
|
198
|
+
},
|
|
199
|
+
/**
|
|
200
|
+
* Ask AI. The one streaming route here, and the only one that does not
|
|
201
|
+
* answer JSON - it forwards Connect's server-sent event stream through
|
|
202
|
+
* untouched so `<ConnectAskAi>` can render tokens as they arrive.
|
|
203
|
+
*
|
|
204
|
+
* Same rule as every other route: the workspace comes from the caller's
|
|
205
|
+
* own session. It matters more here than anywhere else, because the
|
|
206
|
+
* answers are *retrieved from that workspace's Context Index* - a
|
|
207
|
+
* client-supplied `workspaceId` would be a read of another tenant's
|
|
208
|
+
* documents, laundered through a chat reply.
|
|
209
|
+
*
|
|
210
|
+
* Messages are forwarded as-is (part-based `UIMessage`s), and only their
|
|
211
|
+
* shape is checked: the conversation is the caller's own, and Connect
|
|
212
|
+
* validates it again on arrival.
|
|
213
|
+
*/
|
|
214
|
+
"POST /ai/chat": async (request) => {
|
|
215
|
+
const { user } = await whoami(request);
|
|
216
|
+
const body = (await request.json().catch(() => null));
|
|
217
|
+
if (!Array.isArray(body?.messages)) {
|
|
218
|
+
throw new errors_js_1.ConnectError({
|
|
219
|
+
status: 400,
|
|
220
|
+
code: "validation_error",
|
|
221
|
+
message: "messages is required",
|
|
222
|
+
endpoint: "POST /ai/chat",
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
const upstream = await client.ai.chat({
|
|
226
|
+
workspaceId: user.workspaceId,
|
|
227
|
+
messages: body.messages,
|
|
228
|
+
});
|
|
229
|
+
// `upstream.body` is piped, never buffered - awaiting it would collect
|
|
230
|
+
// the whole answer server-side and deliver it in one lump, which is the
|
|
231
|
+
// only thing streaming was for. The browser cancelling its read
|
|
232
|
+
// propagates down this pipe and stops generation upstream.
|
|
233
|
+
return new Response(upstream.body, {
|
|
234
|
+
status: upstream.status,
|
|
235
|
+
headers: {
|
|
236
|
+
"content-type": upstream.headers.get("content-type") ?? "text/event-stream",
|
|
237
|
+
"cache-control": "no-store",
|
|
238
|
+
"x-content-type-options": "nosniff",
|
|
239
|
+
},
|
|
240
|
+
});
|
|
198
241
|
},
|
|
199
242
|
"GET /integrations": async (request) => {
|
|
200
243
|
const { user } = await whoami(request);
|
|
201
|
-
return json({ toolkits: await client.integrations.list(user.
|
|
244
|
+
return json({ toolkits: await client.integrations.list(user.workspaceId) });
|
|
202
245
|
},
|
|
203
246
|
"POST /integrations/connect": async (request) => {
|
|
204
247
|
const { user } = await whoami(request);
|
|
@@ -211,7 +254,7 @@ function createConnectHandler(options) {
|
|
|
211
254
|
endpoint: "POST /integrations/connect",
|
|
212
255
|
});
|
|
213
256
|
}
|
|
214
|
-
return json(await client.integrations.initiateConnect(user.
|
|
257
|
+
return json(await client.integrations.initiateConnect(user.workspaceId, body.toolkitSlug, body.returnUrl));
|
|
215
258
|
},
|
|
216
259
|
"POST /integrations/disconnect": async (request) => {
|
|
217
260
|
const { user } = await whoami(request);
|
|
@@ -224,10 +267,10 @@ function createConnectHandler(options) {
|
|
|
224
267
|
endpoint: "POST /integrations/disconnect",
|
|
225
268
|
});
|
|
226
269
|
}
|
|
227
|
-
await client.integrations.disconnect(user.
|
|
270
|
+
await client.integrations.disconnect(user.workspaceId, body.toolkitSlug);
|
|
228
271
|
return json({ ok: true });
|
|
229
272
|
},
|
|
230
|
-
// Account-wide, not scoped to this
|
|
273
|
+
// Account-wide, not scoped to this application - see `SessionsResource`'s
|
|
231
274
|
// doc comment. `requireToken` alone identifies the caller; the target
|
|
232
275
|
// `sessionId` on the revoke routes travels in the JSON body, not the
|
|
233
276
|
// URL, so the exact-match routes table above never needs a dynamic
|
package/dist/cjs/server/http.js
CHANGED
|
@@ -44,7 +44,7 @@ function parseRetryAfterMs(response) {
|
|
|
44
44
|
return Number.isFinite(seconds) ? Math.max(0, seconds) * 1000 : null;
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
|
-
* Both error dialects, one shape. `/api/v1/
|
|
47
|
+
* Both error dialects, one shape. `/api/v1/application/*` answers
|
|
48
48
|
* `{ error: { code, message } }`; the `/sso/*` endpoints answer OAuth2's
|
|
49
49
|
* `{ error, error_description }`.
|
|
50
50
|
*/
|
|
@@ -128,33 +128,55 @@ async function request(options, spec) {
|
|
|
128
128
|
// request for minutes.
|
|
129
129
|
await sleep(Math.min(lastError?.retryAfterMs ?? 250 * 2 ** (attempt - 1), 8_000));
|
|
130
130
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
// A cancellable timer rather than `AbortSignal.timeout`, because the
|
|
132
|
+
// timeout has to be *stopped* once this attempt is done reading - and a
|
|
133
|
+
// timeout signal cannot be cleared.
|
|
134
|
+
//
|
|
135
|
+
// That distinction is the whole point for a streaming endpoint. `fetch`
|
|
136
|
+
// resolves the moment the headers land, but its signal keeps governing
|
|
137
|
+
// the body: an `AbortSignal.timeout(20_000)` handed to `ai.chat` tears the
|
|
138
|
+
// SSE stream down mid-answer, twenty seconds in, on every conversation
|
|
139
|
+
// long enough to matter. Clearing the timer in `finally` bounds the
|
|
140
|
+
// request the same way it always did (a `raw` caller still gets its
|
|
141
|
+
// timeout on *reaching* Connect, and a JSON caller still gets it across
|
|
142
|
+
// the body read, which happens inside the `try`) while leaving a stream
|
|
143
|
+
// that has actually started to run as long as it needs. A caller that
|
|
144
|
+
// wants to bound the stream itself passes `spec.signal`.
|
|
145
|
+
const timeoutController = new AbortController();
|
|
146
|
+
const timer = setTimeout(() => timeoutController.abort(), options.timeoutMs);
|
|
147
|
+
const signal = spec.signal
|
|
148
|
+
? AbortSignal.any([spec.signal, timeoutController.signal])
|
|
149
|
+
: timeoutController.signal;
|
|
134
150
|
try {
|
|
135
|
-
response
|
|
151
|
+
let response;
|
|
152
|
+
try {
|
|
153
|
+
response = await options.fetch(url.toString(), { method: spec.method, headers, body, signal });
|
|
154
|
+
}
|
|
155
|
+
catch (cause) {
|
|
156
|
+
// A caller-supplied abort is a decision, not a fault - never retried.
|
|
157
|
+
if (spec.signal?.aborted)
|
|
158
|
+
throw cause;
|
|
159
|
+
lastError = new errors_js_1.ConnectError({
|
|
160
|
+
status: 0,
|
|
161
|
+
code: "network_error",
|
|
162
|
+
message: `${endpoint} could not reach Connect`,
|
|
163
|
+
endpoint,
|
|
164
|
+
cause,
|
|
165
|
+
});
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (response.ok) {
|
|
169
|
+
if (spec.raw)
|
|
170
|
+
return response;
|
|
171
|
+
return (await readBody(response));
|
|
172
|
+
}
|
|
173
|
+
lastError = toConnectError(response, await readBody(response), endpoint);
|
|
174
|
+
if (!RETRYABLE_STATUS.has(response.status))
|
|
175
|
+
throw lastError;
|
|
136
176
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if (spec.signal?.aborted)
|
|
140
|
-
throw cause;
|
|
141
|
-
lastError = new errors_js_1.ConnectError({
|
|
142
|
-
status: 0,
|
|
143
|
-
code: "network_error",
|
|
144
|
-
message: `${endpoint} could not reach Connect`,
|
|
145
|
-
endpoint,
|
|
146
|
-
cause,
|
|
147
|
-
});
|
|
148
|
-
continue;
|
|
177
|
+
finally {
|
|
178
|
+
clearTimeout(timer);
|
|
149
179
|
}
|
|
150
|
-
if (response.ok) {
|
|
151
|
-
if (spec.raw)
|
|
152
|
-
return response;
|
|
153
|
-
return (await readBody(response));
|
|
154
|
-
}
|
|
155
|
-
lastError = toConnectError(response, await readBody(response), endpoint);
|
|
156
|
-
if (!RETRYABLE_STATUS.has(response.status))
|
|
157
|
-
throw lastError;
|
|
158
180
|
}
|
|
159
181
|
throw lastError;
|
|
160
182
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AccessResource = void 0;
|
|
4
4
|
const http_js_1 = require("../http.js");
|
|
5
|
-
/** What a signed-in member may do on your
|
|
5
|
+
/** What a signed-in member may do on your application, and the role catalog behind it. */
|
|
6
6
|
class AccessResource {
|
|
7
7
|
transport;
|
|
8
8
|
constructor(transport) {
|
|
@@ -17,10 +17,10 @@ class AccessResource {
|
|
|
17
17
|
return (0, http_js_1.request)(this.transport, {
|
|
18
18
|
method: "GET",
|
|
19
19
|
path: `/rbac/users/${(0, http_js_1.pathSegment)(userId, "userId")}/roles`,
|
|
20
|
-
query: {
|
|
20
|
+
query: { workspaceId: input.workspaceId },
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
|
-
/** Your
|
|
23
|
+
/** Your application's own permission catalog and role definitions - the definitions `getUserRoles` resolves against. */
|
|
24
24
|
listRoles() {
|
|
25
25
|
return (0, http_js_1.request)(this.transport, { method: "GET", path: "/roles" });
|
|
26
26
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AiResource = void 0;
|
|
4
4
|
const http_js_1 = require("../http.js");
|
|
5
|
-
/** RAG chat and structured extraction against
|
|
5
|
+
/** RAG chat and structured extraction against a workspace's data. */
|
|
6
6
|
class AiResource {
|
|
7
7
|
transport;
|
|
8
8
|
constructor(transport) {
|
|
@@ -3,10 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.AnalyticsResource = void 0;
|
|
4
4
|
const http_js_1 = require("../http.js");
|
|
5
5
|
/**
|
|
6
|
-
* Your
|
|
6
|
+
* Your application's own analytics: emit product events, and read back the daily
|
|
7
7
|
* numbers Connect aggregates from them.
|
|
8
8
|
*
|
|
9
|
-
* There is no way to name
|
|
9
|
+
* There is no way to name an application in either call. The scope is always the one
|
|
10
10
|
* your API key belongs to - that is the endpoint's security model, not an
|
|
11
11
|
* omission.
|
|
12
12
|
*/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AppsResource = void 0;
|
|
4
4
|
const http_js_1 = require("../http.js");
|
|
5
|
-
/** The
|
|
5
|
+
/** The applications a workspace can open - the data behind a launcher grid. */
|
|
6
6
|
class AppsResource {
|
|
7
7
|
transport;
|
|
8
8
|
constructor(transport) {
|
|
@@ -10,13 +10,13 @@ class AppsResource {
|
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
12
|
* Launchable apps only, so a grid tile is never a dead end. The calling
|
|
13
|
-
*
|
|
13
|
+
* application is included and flagged `isCurrent` rather than filtered out.
|
|
14
14
|
*/
|
|
15
|
-
async list(
|
|
15
|
+
async list(workspaceId) {
|
|
16
16
|
const result = await (0, http_js_1.request)(this.transport, {
|
|
17
17
|
method: "GET",
|
|
18
18
|
path: "/apps",
|
|
19
|
-
query: {
|
|
19
|
+
query: { workspaceId },
|
|
20
20
|
});
|
|
21
21
|
// `?? []` for the same reason `billing.listPlans` has it: this is a
|
|
22
22
|
// collection every caller immediately maps or measures, and a body without
|
|
@@ -10,7 +10,7 @@ class BillingResource {
|
|
|
10
10
|
this.transport = transport;
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
* Your
|
|
13
|
+
* Your application's active plans - render live pricing instead of hardcoding it.
|
|
14
14
|
*
|
|
15
15
|
* `features` is normalised on the way out: Connect used to send bare keys
|
|
16
16
|
* and now sends `{ key, name, description }`, and this SDK is versioned
|
|
@@ -28,7 +28,7 @@ class BillingResource {
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
|
-
* What is left of every consumable add-on on
|
|
31
|
+
* What is left of every consumable add-on on a workspace's
|
|
32
32
|
* subscription - the read half of `consumeAddon`.
|
|
33
33
|
*
|
|
34
34
|
* Needs a key scoped to `addons:read`, which is separate from
|
|
@@ -38,10 +38,10 @@ class BillingResource {
|
|
|
38
38
|
* Returns an empty `addons` array rather than throwing when there is
|
|
39
39
|
* nothing to meter, so a caller can render it unconditionally.
|
|
40
40
|
*/
|
|
41
|
-
async getAddonUsage(
|
|
41
|
+
async getAddonUsage(workspaceId) {
|
|
42
42
|
const response = await (0, http_js_1.request)(this.transport, {
|
|
43
43
|
method: "GET",
|
|
44
|
-
path: `/addons/
|
|
44
|
+
path: `/addons/workspaces/${(0, http_js_1.pathSegment)(workspaceId, "workspaceId")}`,
|
|
45
45
|
});
|
|
46
46
|
// Normalised, not passed through: the promise above resolves for any 2xx,
|
|
47
47
|
// and `ConnectUsageMeter` reads `addons` straight off it. A body without
|
|
@@ -52,7 +52,7 @@ class BillingResource {
|
|
|
52
52
|
}
|
|
53
53
|
/**
|
|
54
54
|
* Debit a consumable add-on's per-cycle balance on behalf of an
|
|
55
|
-
*
|
|
55
|
+
* workspace's users. Call it at whatever cadence your usage happens.
|
|
56
56
|
*
|
|
57
57
|
* An `Idempotency-Key` is always sent, generated per call when you do not
|
|
58
58
|
* supply one: a balance cannot be un-spent, so a retried consume without a
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DriveResource = void 0;
|
|
4
4
|
const http_js_1 = require("../http.js");
|
|
5
|
-
/** Files in
|
|
5
|
+
/** Files in a workspace's Drive - upload, and signed URLs to read back. */
|
|
6
6
|
class DriveResource {
|
|
7
7
|
transport;
|
|
8
8
|
constructor(transport) {
|
|
@@ -12,7 +12,7 @@ class DriveResource {
|
|
|
12
12
|
const form = new FormData();
|
|
13
13
|
const fileName = input.fileName ?? input.file.name ?? "upload";
|
|
14
14
|
form.set("file", input.file, fileName);
|
|
15
|
-
form.set("
|
|
15
|
+
form.set("workspaceId", input.workspaceId);
|
|
16
16
|
if (input.parentId)
|
|
17
17
|
form.set("parentId", input.parentId);
|
|
18
18
|
return (0, http_js_1.request)(this.transport, { method: "POST", path: "/drive/files", multipart: form });
|
|
@@ -22,7 +22,7 @@ class DriveResource {
|
|
|
22
22
|
const result = await (0, http_js_1.request)(this.transport, {
|
|
23
23
|
method: "GET",
|
|
24
24
|
path: `/drive/files/${(0, http_js_1.pathSegment)(fileId, "fileId")}/url`,
|
|
25
|
-
query: {
|
|
25
|
+
query: { workspaceId: input.workspaceId },
|
|
26
26
|
});
|
|
27
27
|
return result.url;
|
|
28
28
|
}
|
|
@@ -31,7 +31,7 @@ class DriveResource {
|
|
|
31
31
|
const result = await (0, http_js_1.request)(this.transport, {
|
|
32
32
|
method: "GET",
|
|
33
33
|
path: `/drive/files/${(0, http_js_1.pathSegment)(fileId, "fileId")}/preview-url`,
|
|
34
|
-
query: {
|
|
34
|
+
query: { workspaceId: input.workspaceId, size: input.size },
|
|
35
35
|
});
|
|
36
36
|
return result.url;
|
|
37
37
|
}
|
|
@@ -12,7 +12,7 @@ class EntitlementsResource {
|
|
|
12
12
|
this.transport = transport;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* Every gate Connect owns between
|
|
15
|
+
* Every gate Connect owns between a workspace and your application -
|
|
16
16
|
* subscription, KYC/KYB, a chargeable payment method, seat headroom -
|
|
17
17
|
* resolved in one call.
|
|
18
18
|
*
|
|
@@ -20,17 +20,17 @@ class EntitlementsResource {
|
|
|
20
20
|
* Connect screen that fixes it, so you can route them somewhere useful
|
|
21
21
|
* instead of refusing them with nowhere to go.
|
|
22
22
|
*/
|
|
23
|
-
getReadiness(
|
|
23
|
+
getReadiness(workspaceId) {
|
|
24
24
|
return (0, http_js_1.request)(this.transport, {
|
|
25
25
|
method: "GET",
|
|
26
|
-
path: `/onboarding/
|
|
26
|
+
path: `/onboarding/workspaces/${(0, http_js_1.pathSegment)(workspaceId, "workspaceId")}`,
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
/** The live plan and subscription, including `plan.features`. */
|
|
30
|
-
getSubscription(
|
|
30
|
+
getSubscription(workspaceId) {
|
|
31
31
|
return (0, http_js_1.request)(this.transport, {
|
|
32
32
|
method: "GET",
|
|
33
|
-
path: `/subscriptions/
|
|
33
|
+
path: `/subscriptions/workspaces/${(0, http_js_1.pathSegment)(workspaceId, "workspaceId")}`,
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
@@ -41,9 +41,50 @@ class EntitlementsResource {
|
|
|
41
41
|
* all: "not entitled" is the same answer either way, and a feature gate
|
|
42
42
|
* should not need a try/catch around it.
|
|
43
43
|
*/
|
|
44
|
-
async hasFeature(
|
|
45
|
-
const result = await this.getSubscription(
|
|
44
|
+
async hasFeature(workspaceId, feature) {
|
|
45
|
+
const result = await this.getSubscription(workspaceId);
|
|
46
46
|
return result.hasActiveSubscription && (result.plan?.features.includes(feature) ?? false);
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* One page of the workspaces subscribed to your application, newest first.
|
|
50
|
+
*
|
|
51
|
+
* The inverse of `getSubscription`, which asks about one workspace you
|
|
52
|
+
* already know. This asks who your subscribers *are* - what a partner
|
|
53
|
+
* dashboard, an MRR total or a churn report is built on.
|
|
54
|
+
*
|
|
55
|
+
* Rate-limited far more tightly than the per-workspace read (10/min per
|
|
56
|
+
* key, against 60): it walks your whole subscriber base and is a reporting
|
|
57
|
+
* call, not a per-request gate. Use `getSubscription` in a hot path.
|
|
58
|
+
*
|
|
59
|
+
* Your application is resolved from the API key, so there is no parameter
|
|
60
|
+
* for it and no way to ask about anyone else's.
|
|
61
|
+
*/
|
|
62
|
+
listSubscribers(options = {}) {
|
|
63
|
+
return (0, http_js_1.request)(this.transport, {
|
|
64
|
+
method: "GET",
|
|
65
|
+
path: "/subscriptions",
|
|
66
|
+
query: { status: options.status, before: options.before, limit: options.limit },
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Every subscriber, paged through for you.
|
|
71
|
+
*
|
|
72
|
+
* An async generator rather than an array: a large subscriber base is
|
|
73
|
+
* exactly where an accidental full materialisation hurts, and `for await`
|
|
74
|
+
* reads the same as a loop over one page.
|
|
75
|
+
*/
|
|
76
|
+
async *iterateSubscribers(options = {}) {
|
|
77
|
+
let before;
|
|
78
|
+
do {
|
|
79
|
+
const page = await this.listSubscribers({
|
|
80
|
+
status: options.status,
|
|
81
|
+
limit: options.pageSize,
|
|
82
|
+
before,
|
|
83
|
+
});
|
|
84
|
+
for (const item of page.items)
|
|
85
|
+
yield item;
|
|
86
|
+
before = page.nextBefore ?? undefined;
|
|
87
|
+
} while (before);
|
|
88
|
+
}
|
|
48
89
|
}
|
|
49
90
|
exports.EntitlementsResource = EntitlementsResource;
|
|
@@ -70,7 +70,8 @@ class IdentityResource {
|
|
|
70
70
|
sessionTokenExpiresIn: wire.session_token_expires_in,
|
|
71
71
|
user: wire.user,
|
|
72
72
|
subscription: wire.subscription,
|
|
73
|
-
|
|
73
|
+
applicationRole: wire.applicationRole,
|
|
74
|
+
audience: wire.audience,
|
|
74
75
|
};
|
|
75
76
|
}
|
|
76
77
|
/**
|
|
@@ -140,7 +141,8 @@ class IdentityResource {
|
|
|
140
141
|
return {
|
|
141
142
|
user: wire.user,
|
|
142
143
|
subscription: wire.subscription,
|
|
143
|
-
|
|
144
|
+
applicationRole: wire.applicationRole,
|
|
145
|
+
audience: wire.audience,
|
|
144
146
|
sessionExpiresAt: wire.session_expires_at,
|
|
145
147
|
};
|
|
146
148
|
}
|
|
@@ -149,7 +151,7 @@ class IdentityResource {
|
|
|
149
151
|
* rest of its ~15-day life. Discard your own copy of `sessionToken` too;
|
|
150
152
|
* this does not do that for you.
|
|
151
153
|
*
|
|
152
|
-
* `scope` defaults to `"device"`: this sign-in, every other
|
|
154
|
+
* `scope` defaults to `"device"`: this sign-in, every other application signed
|
|
153
155
|
* into from the same browser, and Connect's own session on that one device.
|
|
154
156
|
* `"all"` ends every session on every device - an explicit "sign out
|
|
155
157
|
* everywhere" action only.
|
|
@@ -181,7 +183,7 @@ class IdentityResource {
|
|
|
181
183
|
throw new errors_js_1.ConnectError({
|
|
182
184
|
status: 0,
|
|
183
185
|
code: "missing_client_id",
|
|
184
|
-
message: "identity.* needs `clientId`. Pass it to `new ConnectClient({ clientId, apiKey })` - it is shown in this
|
|
186
|
+
message: "identity.* needs `clientId`. Pass it to `new ConnectClient({ clientId, apiKey })` - it is shown in this application's Integration tab, under Connect client.",
|
|
185
187
|
endpoint: "identity",
|
|
186
188
|
});
|
|
187
189
|
}
|
|
@@ -4,8 +4,8 @@ exports.IntegrationsResource = void 0;
|
|
|
4
4
|
const http_js_1 = require("../http.js");
|
|
5
5
|
/**
|
|
6
6
|
* Composio-hosted third-party connections (Google Calendar, Slack,
|
|
7
|
-
* Twitter/X, ...)
|
|
8
|
-
* here - your
|
|
7
|
+
* Twitter/X, ...) a workspace has connected. Connect proxies every call
|
|
8
|
+
* here - your application never sees or holds the underlying OAuth token, which
|
|
9
9
|
* stays with Composio.
|
|
10
10
|
*/
|
|
11
11
|
class IntegrationsResource {
|
|
@@ -13,12 +13,12 @@ class IntegrationsResource {
|
|
|
13
13
|
constructor(transport) {
|
|
14
14
|
this.transport = transport;
|
|
15
15
|
}
|
|
16
|
-
/** Enabled toolkits and this
|
|
17
|
-
async list(
|
|
16
|
+
/** Enabled toolkits and this workspace's connection status for each. */
|
|
17
|
+
async list(workspaceId) {
|
|
18
18
|
const result = await (0, http_js_1.request)(this.transport, {
|
|
19
19
|
method: "GET",
|
|
20
20
|
path: "/integrations",
|
|
21
|
-
query: {
|
|
21
|
+
query: { workspaceId },
|
|
22
22
|
});
|
|
23
23
|
// `?? []` for the same reason `AppsResource.list` has it - a collection
|
|
24
24
|
// every caller immediately maps or measures.
|
|
@@ -35,31 +35,31 @@ class IntegrationsResource {
|
|
|
35
35
|
* itself. Reconnecting a disconnected/failed toolkit calls this same
|
|
36
36
|
* method - there is no separate reconnect.
|
|
37
37
|
*
|
|
38
|
-
* Meant to be called on behalf of a signed-in
|
|
38
|
+
* Meant to be called on behalf of a signed-in workspace admin (this is what the
|
|
39
39
|
* bridge's fixed `POST /integrations/connect` route does), not as a bare
|
|
40
40
|
* background action with no admin present to complete the OAuth screen.
|
|
41
41
|
*/
|
|
42
|
-
async initiateConnect(
|
|
42
|
+
async initiateConnect(workspaceId, toolkitSlug, returnUrl) {
|
|
43
43
|
return (0, http_js_1.request)(this.transport, {
|
|
44
44
|
method: "POST",
|
|
45
45
|
path: "/integrations/connect",
|
|
46
|
-
json: {
|
|
46
|
+
json: { workspaceId, toolkitSlug, returnUrl },
|
|
47
47
|
});
|
|
48
48
|
}
|
|
49
49
|
/** Idempotent - disconnecting a toolkit that was never connected, or is already disconnected, is a success. */
|
|
50
|
-
async disconnect(
|
|
50
|
+
async disconnect(workspaceId, toolkitSlug) {
|
|
51
51
|
return (0, http_js_1.request)(this.transport, {
|
|
52
52
|
method: "POST",
|
|
53
53
|
path: "/integrations/disconnect",
|
|
54
|
-
json: {
|
|
54
|
+
json: { workspaceId, toolkitSlug },
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
|
-
/** Calls a Composio action against this
|
|
58
|
-
async execute(
|
|
57
|
+
/** Calls a Composio action against this workspace's connected account for the named toolkit. */
|
|
58
|
+
async execute(workspaceId, input) {
|
|
59
59
|
return (0, http_js_1.request)(this.transport, {
|
|
60
60
|
method: "POST",
|
|
61
61
|
path: "/integrations/actions/execute",
|
|
62
|
-
json: {
|
|
62
|
+
json: { workspaceId, ...input },
|
|
63
63
|
});
|
|
64
64
|
}
|
|
65
65
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LogsResource = void 0;
|
|
4
4
|
const http_js_1 = require("../http.js");
|
|
5
|
-
/** Push feature events into the Console's audit log, filterable to your
|
|
5
|
+
/** Push feature events into the Console's audit log, filterable to your application. */
|
|
6
6
|
class LogsResource {
|
|
7
7
|
transport;
|
|
8
8
|
constructor(transport) {
|
|
@@ -14,9 +14,9 @@ class LogsResource {
|
|
|
14
14
|
* reported through the return value.
|
|
15
15
|
*
|
|
16
16
|
* Pass `sessionToken` to attribute the event to the end user it belongs to.
|
|
17
|
-
* Both scopes on the stored entry come from something verified - the
|
|
17
|
+
* Both scopes on the stored entry come from something verified - the application
|
|
18
18
|
* from your API key, the user from that token, which Connect checks was
|
|
19
|
-
* minted for your
|
|
19
|
+
* minted for your application before attributing anything. `attributed` in the
|
|
20
20
|
* result says whether it resolved; a `false` there still means the line was
|
|
21
21
|
* stored, just without a user on it.
|
|
22
22
|
*/
|
|
@@ -8,11 +8,11 @@ const http_js_1 = require("../http.js");
|
|
|
8
8
|
* timestamps - the same list Connect's own account settings page shows the
|
|
9
9
|
* user themselves.
|
|
10
10
|
*
|
|
11
|
-
* **Account-wide, not scoped to this
|
|
12
|
-
* browser's login to Connect, and it fans out to every partner
|
|
13
|
-
* `sessionToken` - Connect has no per-
|
|
11
|
+
* **Account-wide, not scoped to this application.** A Connect session is one
|
|
12
|
+
* browser's login to Connect, and it fans out to every partner application's own
|
|
13
|
+
* `sessionToken` - Connect has no per-application session concept to filter by.
|
|
14
14
|
* `list()` returns every session on the account, including ones that
|
|
15
|
-
* originated from a completely different, unrelated
|
|
15
|
+
* originated from a completely different, unrelated application. Requires the
|
|
16
16
|
* `sessions:manage` scope on your API key - unlike `identity`'s base `sso`
|
|
17
17
|
* capability, this is never granted to a zero-scope legacy key by default.
|
|
18
18
|
*
|
|
@@ -38,7 +38,7 @@ class SessionsResource {
|
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
40
|
* Ends one specific session by id - not necessarily the one `sessionToken`
|
|
41
|
-
* itself was minted from. Cascades to every partner-
|
|
41
|
+
* itself was minted from. Cascades to every partner-application sign-in that
|
|
42
42
|
* originated from that same browser, same as Connect's own "sign out this
|
|
43
43
|
* device".
|
|
44
44
|
*/
|
|
@@ -72,7 +72,7 @@ class SessionsResource {
|
|
|
72
72
|
throw new errors_js_1.ConnectError({
|
|
73
73
|
status: 0,
|
|
74
74
|
code: "missing_client_id",
|
|
75
|
-
message: "sessions.* needs `clientId`. Pass it to `new ConnectClient({ clientId, apiKey })` - it is shown in this
|
|
75
|
+
message: "sessions.* needs `clientId`. Pass it to `new ConnectClient({ clientId, apiKey })` - it is shown in this application's Integration tab, under Connect client.",
|
|
76
76
|
endpoint: "sessions",
|
|
77
77
|
});
|
|
78
78
|
}
|
|
@@ -58,10 +58,10 @@ function verifyConnectWebhook(rawBody, signatureHeader, secret, now = new Date()
|
|
|
58
58
|
* export const POST = createConnectWebhookHandler({
|
|
59
59
|
* secret: process.env.CONNECT_WEBHOOK_SECRET!,
|
|
60
60
|
* on: {
|
|
61
|
-
* "
|
|
62
|
-
* freeze(
|
|
63
|
-
* "
|
|
64
|
-
* "
|
|
61
|
+
* "workspace.deletion_scheduled": ({ workspaceId, purgeAfter }) =>
|
|
62
|
+
* freeze(workspaceId, purgeAfter),
|
|
63
|
+
* "workspace.deletion_cancelled": ({ workspaceId }) => unfreeze(workspaceId),
|
|
64
|
+
* "workspace.deleted": ({ workspaceId }) => purge(workspaceId),
|
|
65
65
|
* },
|
|
66
66
|
* })
|
|
67
67
|
* ```
|
package/dist/cjs/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Wire types for the `/api/v1/
|
|
3
|
+
* Wire types for the `/api/v1/application/*` surface - the application-partner API,
|
|
4
4
|
* and the only thing this SDK covers.
|
|
5
5
|
*
|
|
6
6
|
* Hand-written rather than generated so the published package carries no
|