@agentforge-io/sdk-server 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { AgentForgeClient } from './client';
2
2
  export type { AgentForgeClientOptions, RequestOptions } from './client';
3
3
  export { AgentForgeError, AgentForgeAuthError, AgentForgeNotFoundError, AgentForgeNetworkError, } from './errors';
4
4
  export { TOOL_CALLBACK_HEADERS, ToolCallbackSignatureError, signToolCallbackPayload, verifyToolCallbackSignature, } from './webhooks';
5
- export type { AgentMe, AgentSummary, ApprovalCopyBundle, ChatStreamChunk, ChatStreamEvent, ChatTokenRecord, ConnectorDefaultsResult, ConnectorDefaultsSource, ConnectorResourceItem, ConnectorStatus, ConnectorToolInfo, ConversationHistory, ConversationStatus, CreateAgentInput, CreateClickupTaskInput, CreateClickupTaskResult, ExternalToolSummary, IssueChatTokenInput, ListAgentsResult, ListConnectorResourcesResult, McpServerConfig, MessageRecord, MessageRole, PublicAppBranding, AppSelf, AppSelfConnector, AppSelfConnectors, RegisterToolInput, RegisterToolsInput, RegisterToolsResult, ToolCallbackPayload, ToolCallRecord, ToolPermission, ToolPermissionMode, TokenUsage, TenantSecretEntry, UpdateAgentInput, } from './types';
5
+ export type { AgentMe, AgentSummary, AppUserConnectorStatus, ApprovalCopyBundle, ChatStreamChunk, ChatStreamEvent, ChatTokenRecord, ConnectorDefaultsResult, ConnectorDefaultsSource, ConnectorResourceItem, ConnectorStatus, ConnectorToolInfo, ConversationHistory, ConversationStatus, CreateAgentInput, CreateClickupTaskInput, CreateClickupTaskResult, ExternalToolSummary, IssueChatTokenInput, ListAgentsResult, ListConnectorResourcesResult, McpServerConfig, MessageRecord, MessageRole, PublicAppBranding, AppSelf, AppSelfConnector, AppSelfConnectors, RegisterToolInput, RegisterToolsInput, RegisterToolsResult, ToolCallbackPayload, ToolCallRecord, ToolPermission, ToolPermissionMode, TokenUsage, TenantSecretEntry, UpdateAgentInput, } from './types';
6
6
  export { AgentsResource } from './resources/agents';
7
7
  export { AppsResource } from './resources/apps';
8
8
  export { ChatResource } from './resources/chat';
@@ -1,5 +1,5 @@
1
1
  import type { AgentForgeClient } from '../client';
2
- import type { ConnectorDefaultsResult, ConnectorStatus, CreateClickupTaskInput, CreateClickupTaskResult, ListConnectorResourcesResult, ToolPermission } from '../types';
2
+ import type { AppUserConnectorStatus, ConnectorDefaultsResult, ConnectorStatus, CreateClickupTaskInput, CreateClickupTaskResult, ListConnectorResourcesResult, ToolPermission } from '../types';
3
3
  export declare class ConnectorsResource {
4
4
  private readonly client;
5
5
  constructor(client: AgentForgeClient);
@@ -196,6 +196,73 @@ export declare class ConnectorsResource {
196
196
  * desconecta un canal del inbox omnicanal.
197
197
  */
198
198
  destroyInboxChannel(agentId: string, channelId: string): Promise<void>;
199
+ /**
200
+ * `POST /connectors/oauth/:id/authorize-for-app-user` — starts the
201
+ * connector OAuth flow on behalf of ONE end user of the `sk_live_`/
202
+ * `sk_test_` App (`appId` resolved from the secret, `externalUserId`
203
+ * explicit). App-user counterpart of `authorizeForTenant()` above — same
204
+ * response shape (`{ url }`, open it e.g. via `window.open` in the
205
+ * consumer app), same callback flow end-to-end on the platform.
206
+ *
207
+ * Requires the `AgentForgeClient` to have been constructed with
208
+ * `appSecret` — throws early otherwise (this surface authenticates AS the
209
+ * App, not via ak_live_/ChatToken).
210
+ *
211
+ * @example
212
+ * const af = new AgentForgeClient({ baseURL, appSecret: 'sk_live_...' });
213
+ * const { url } = await af.connectors.authorizeForAppUser('slack', {
214
+ * externalUserId: 'scrappydoc-user-42',
215
+ * postAuthRedirect: 'https://myapp.com/settings',
216
+ * });
217
+ */
218
+ authorizeForAppUser(connectorId: string, input: {
219
+ externalUserId: string;
220
+ postAuthRedirect?: string;
221
+ }): Promise<{
222
+ url: string;
223
+ }>;
224
+ /**
225
+ * `GET /connectors/oauth/app-user/directory` — the given end user's
226
+ * connectors + connection status (App-user counterpart of
227
+ * `listConnected()`, which is the ak_live_ tenant-wide picker). Returns
228
+ * the SAME rich shape `listForTenant`/`listForUser` produce server-side
229
+ * (`AppUserConnectorStatus`: name/description/category/iconUrl/configured/
230
+ * accountLabel/connectedAt) — NOT the narrow `ConnectorStatus` the
231
+ * tenant-picker methods above use.
232
+ *
233
+ * Requires `appSecret` — throws early otherwise, same as
234
+ * `authorizeForAppUser`.
235
+ *
236
+ * @example
237
+ * const { items } = await af.connectors.listConnectorsForAppUser({
238
+ * externalUserId: 'scrappydoc-user-42',
239
+ * });
240
+ * const unconnected = items.filter((c) => c.configured && !c.connected);
241
+ */
242
+ listConnectorsForAppUser(input: {
243
+ externalUserId: string;
244
+ }): Promise<{
245
+ items: AppUserConnectorStatus[];
246
+ }>;
247
+ /**
248
+ * `DELETE /connectors/oauth/:id/app-user` — disconnects `connectorId` for
249
+ * the given end user. App-user counterpart of `disconnect()` above.
250
+ * Mirrors the platform's `disconnectForAppUser` 404-on-missing behavior
251
+ * (`ConnectorError('not_connected', ...)` → `AgentForgeNotFoundError`).
252
+ *
253
+ * Requires `appSecret` — throws early otherwise, same as
254
+ * `authorizeForAppUser`.
255
+ *
256
+ * @example
257
+ * await af.connectors.disconnectForAppUser('google-drive', {
258
+ * externalUserId: 'scrappydoc-user-42',
259
+ * });
260
+ */
261
+ disconnectForAppUser(connectorId: string, input: {
262
+ externalUserId: string;
263
+ }): Promise<{
264
+ ok: true;
265
+ }>;
199
266
  }
200
267
  export interface InboxChannelView {
201
268
  id: string;
@@ -5,9 +5,13 @@
5
5
  // reales de uno conectado (canales de Slack, repos de GitHub, etc), y arranca
6
6
  // el OAuth authorize para uno que falte conectar.
7
7
  //
8
- // Auth: SIEMPRE ak_live_ tenant-wide — estos 3 endpoints usan `ApiKeyGuard`
9
- // en el server y rechazan ChatToken de plano, aunque el `AgentForgeClient`
10
- // también tenga uno configurado (ver `requestWithApiKey`).
8
+ // Auth: la mayoría de los métodos son ak_live_ tenant-wide — usan
9
+ // `ApiKeyGuard` en el server y rechazan ChatToken de plano, aunque el
10
+ // `AgentForgeClient` también tenga uno configurado (ver `requestWithApiKey`).
11
+ // EXCEPCIÓN (AGF-072, sección "App-user scope" al final del archivo): los
12
+ // métodos `*ForAppUser` (Plane B — un end user del App conectando SU PROPIA
13
+ // cuenta) usan `appSecret` (sk_live_/sk_test_) en vez de ak_live_, y por eso
14
+ // llaman `this.client.request()` directo en lugar de `requestWithApiKey()`.
11
15
  Object.defineProperty(exports, "__esModule", { value: true });
12
16
  exports.ConnectorsResource = void 0;
13
17
  class ConnectorsResource {
@@ -203,5 +207,99 @@ class ConnectorsResource {
203
207
  async destroyInboxChannel(agentId, channelId) {
204
208
  await this.client.requestWithApiKey('DELETE', `/connectors/inbox/agents/${encodeURIComponent(agentId)}/inbox/channels/${encodeURIComponent(channelId)}`);
205
209
  }
210
+ // ─── App-user scope (AGF-072, Option A — APP_END_USER_CONNECTIONS_SDD.md §4.4) ──
211
+ //
212
+ // The 3 methods below are Plane B: a specific END USER of an `sk_live_`/
213
+ // `sk_test_` App connecting THEIR OWN account (their Slack, their Gmail) —
214
+ // distinct from `*ForTenant` above (Plane A, the App's own tenant-wide
215
+ // credentials) and from the ak_live_-only picker methods at the top of this
216
+ // file. Auth is `appSecret` (sk_live_/sk_test_), NOT ak_live_ — these calls
217
+ // use `this.client.request()` directly (plain `authHeaders()`, which picks
218
+ // `appSecret` when set) instead of `requestWithApiKey()`, because that
219
+ // helper hardcodes `forceApiKey: true` and would reject an appSecret-only
220
+ // client. Same pattern as `ChatResource.streamMessageForAppUser`.
221
+ //
222
+ // `appId` is NEVER a param on any of these — the platform resolves it
223
+ // exclusively from the `sk_live_`/`sk_test_` secret server-side (SDD §4.4),
224
+ // so App Y's secret can never touch App X's grants no matter what
225
+ // `externalUserId` is passed. Only `externalUserId` (the App's own,
226
+ // unsigned identifier for its end user) travels as a param.
227
+ /**
228
+ * `POST /connectors/oauth/:id/authorize-for-app-user` — starts the
229
+ * connector OAuth flow on behalf of ONE end user of the `sk_live_`/
230
+ * `sk_test_` App (`appId` resolved from the secret, `externalUserId`
231
+ * explicit). App-user counterpart of `authorizeForTenant()` above — same
232
+ * response shape (`{ url }`, open it e.g. via `window.open` in the
233
+ * consumer app), same callback flow end-to-end on the platform.
234
+ *
235
+ * Requires the `AgentForgeClient` to have been constructed with
236
+ * `appSecret` — throws early otherwise (this surface authenticates AS the
237
+ * App, not via ak_live_/ChatToken).
238
+ *
239
+ * @example
240
+ * const af = new AgentForgeClient({ baseURL, appSecret: 'sk_live_...' });
241
+ * const { url } = await af.connectors.authorizeForAppUser('slack', {
242
+ * externalUserId: 'scrappydoc-user-42',
243
+ * postAuthRedirect: 'https://myapp.com/settings',
244
+ * });
245
+ */
246
+ async authorizeForAppUser(connectorId, input) {
247
+ if (!this.client.getAppSecret()) {
248
+ throw new Error('connectors.authorizeForAppUser requires appSecret (sk_live_/sk_test_) in ' +
249
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
250
+ }
251
+ const params = new URLSearchParams({ externalUserId: input.externalUserId });
252
+ if (input.postAuthRedirect)
253
+ params.set('postAuthRedirect', input.postAuthRedirect);
254
+ return this.client.request('POST', `/connectors/oauth/${encodeURIComponent(connectorId)}/authorize-for-app-user?${params.toString()}`);
255
+ }
256
+ /**
257
+ * `GET /connectors/oauth/app-user/directory` — the given end user's
258
+ * connectors + connection status (App-user counterpart of
259
+ * `listConnected()`, which is the ak_live_ tenant-wide picker). Returns
260
+ * the SAME rich shape `listForTenant`/`listForUser` produce server-side
261
+ * (`AppUserConnectorStatus`: name/description/category/iconUrl/configured/
262
+ * accountLabel/connectedAt) — NOT the narrow `ConnectorStatus` the
263
+ * tenant-picker methods above use.
264
+ *
265
+ * Requires `appSecret` — throws early otherwise, same as
266
+ * `authorizeForAppUser`.
267
+ *
268
+ * @example
269
+ * const { items } = await af.connectors.listConnectorsForAppUser({
270
+ * externalUserId: 'scrappydoc-user-42',
271
+ * });
272
+ * const unconnected = items.filter((c) => c.configured && !c.connected);
273
+ */
274
+ async listConnectorsForAppUser(input) {
275
+ if (!this.client.getAppSecret()) {
276
+ throw new Error('connectors.listConnectorsForAppUser requires appSecret (sk_live_/sk_test_) in ' +
277
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
278
+ }
279
+ const params = new URLSearchParams({ externalUserId: input.externalUserId });
280
+ return this.client.request('GET', `/connectors/oauth/app-user/directory?${params.toString()}`);
281
+ }
282
+ /**
283
+ * `DELETE /connectors/oauth/:id/app-user` — disconnects `connectorId` for
284
+ * the given end user. App-user counterpart of `disconnect()` above.
285
+ * Mirrors the platform's `disconnectForAppUser` 404-on-missing behavior
286
+ * (`ConnectorError('not_connected', ...)` → `AgentForgeNotFoundError`).
287
+ *
288
+ * Requires `appSecret` — throws early otherwise, same as
289
+ * `authorizeForAppUser`.
290
+ *
291
+ * @example
292
+ * await af.connectors.disconnectForAppUser('google-drive', {
293
+ * externalUserId: 'scrappydoc-user-42',
294
+ * });
295
+ */
296
+ async disconnectForAppUser(connectorId, input) {
297
+ if (!this.client.getAppSecret()) {
298
+ throw new Error('connectors.disconnectForAppUser requires appSecret (sk_live_/sk_test_) in ' +
299
+ 'AgentForgeClient. This surface authenticates AS the App, not via chatToken/apiKey.');
300
+ }
301
+ const params = new URLSearchParams({ externalUserId: input.externalUserId });
302
+ return this.client.request('DELETE', `/connectors/oauth/${encodeURIComponent(connectorId)}/app-user?${params.toString()}`);
303
+ }
206
304
  }
207
305
  exports.ConnectorsResource = ConnectorsResource;
package/dist/types.d.ts CHANGED
@@ -319,6 +319,21 @@ export interface ConnectorStatus {
319
319
  * false = se puede conectar pero `listResources()` devuelve `unsupported`. */
320
320
  browsable: boolean;
321
321
  }
322
+ export interface AppUserConnectorStatus {
323
+ connectorId: string;
324
+ name: string;
325
+ description: string;
326
+ category?: string;
327
+ iconUrl?: string;
328
+ /** El operador del tenant ya trajo credenciales para este provider. Si es
329
+ * `false`, este end user no puede conectar todavía (falta setup admin). */
330
+ configured: boolean;
331
+ /** Este externalUserId particular ya completó el OAuth/API-key setup. */
332
+ connected: boolean;
333
+ accountLabel?: string;
334
+ /** ISO 8601 — `Date` en el server, serializado a string sobre JSON. */
335
+ connectedAt?: string;
336
+ }
322
337
  export interface ConnectorResourceItem {
323
338
  id: string;
324
339
  name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge-io/sdk-server",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "Server-side SDK for backend apps integrating with AgentForge platform. Auth via ChatToken or ak_live_ API key.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",