@planescrm/sdk 0.2.0 → 0.3.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.mts +651 -104
- package/dist/index.d.ts +651 -104
- package/dist/index.js +543 -190
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +531 -187
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,67 @@
|
|
|
1
1
|
import * as http from 'node:http';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Tokens managed by an OAuth-credential client, persisted via {@link TokenStorage}.
|
|
5
5
|
*/
|
|
6
|
-
interface
|
|
6
|
+
interface StoredTokens {
|
|
7
|
+
/** Current OAuth access token. */
|
|
8
|
+
accessToken: string;
|
|
9
|
+
/** Refresh token used to obtain a new access token. */
|
|
10
|
+
refreshToken: string;
|
|
11
|
+
/** Access-token expiry as epoch milliseconds. */
|
|
12
|
+
expiresAt: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Pluggable persistence for OAuth-managed tokens. Implement this to back the
|
|
16
|
+
* client with your own store (encrypted disk, database, secrets manager, ...).
|
|
17
|
+
* The default is {@link MemoryTokenStorage}.
|
|
18
|
+
*/
|
|
19
|
+
interface TokenStorage {
|
|
20
|
+
/** Load the currently stored tokens, or `null` if none are stored. */
|
|
21
|
+
getTokens(): Promise<StoredTokens | null>;
|
|
22
|
+
/** Persist the given tokens (called after every refresh). */
|
|
23
|
+
setTokens(tokens: StoredTokens): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Default in-memory {@link TokenStorage}. Tokens live only for the process
|
|
27
|
+
* lifetime — provide your own implementation to persist across restarts.
|
|
28
|
+
*/
|
|
29
|
+
declare class MemoryTokenStorage implements TokenStorage {
|
|
30
|
+
private tokens;
|
|
31
|
+
constructor(seed?: StoredTokens | null);
|
|
32
|
+
getTokens(): Promise<StoredTokens | null>;
|
|
33
|
+
setTokens(tokens: StoredTokens): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Configuration for the PlanesClient.
|
|
37
|
+
*
|
|
38
|
+
* Exactly one authentication mode must be supplied:
|
|
39
|
+
* - `accessToken` — a pre-obtained OAuth access token (not auto-refreshed)
|
|
40
|
+
* - `apiKey` — a `pk_live_` API key
|
|
41
|
+
* - `clientId` + `clientSecret` — OAuth-managed: the client obtains/refreshes
|
|
42
|
+
* access tokens automatically using `tokenStorage`
|
|
43
|
+
*/
|
|
44
|
+
interface PlanesConfig {
|
|
7
45
|
/** API base URL (defaults to https://api.planescrm.com/api/v1) */
|
|
8
46
|
baseUrl?: string;
|
|
9
|
-
/** OAuth access token (mutually exclusive with apiKey) */
|
|
47
|
+
/** OAuth access token (mutually exclusive with apiKey / clientId+clientSecret) */
|
|
10
48
|
accessToken?: string;
|
|
11
|
-
/** API key (mutually exclusive with accessToken) */
|
|
49
|
+
/** API key (mutually exclusive with accessToken / clientId+clientSecret) */
|
|
12
50
|
apiKey?: string;
|
|
51
|
+
/** OAuth client ID — combined with `clientSecret`, enables the OAuth-managed path */
|
|
52
|
+
clientId?: string;
|
|
53
|
+
/** OAuth client secret — combined with `clientId`, enables the OAuth-managed path */
|
|
54
|
+
clientSecret?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Token store for the OAuth-managed path. Seed it with tokens obtained from
|
|
57
|
+
* an authorization-code exchange. Defaults to {@link MemoryTokenStorage}.
|
|
58
|
+
*/
|
|
59
|
+
tokenStorage?: TokenStorage;
|
|
60
|
+
/**
|
|
61
|
+
* Refresh access tokens proactively when they are within this many
|
|
62
|
+
* milliseconds of expiry (OAuth-managed path only). Default 60000 (60s).
|
|
63
|
+
*/
|
|
64
|
+
tokenRefreshSkewMs?: number;
|
|
13
65
|
/** Request timeout in milliseconds (default 30000) */
|
|
14
66
|
timeout?: number;
|
|
15
67
|
/** Automatically retry on 429 rate limit responses (default true) */
|
|
@@ -20,6 +72,7 @@ interface BlueprintConfig {
|
|
|
20
72
|
declare const DEFAULT_BASE_URL = "https://api.planescrm.com/api/v1";
|
|
21
73
|
declare const DEFAULT_TIMEOUT = 30000;
|
|
22
74
|
declare const DEFAULT_MAX_RETRIES = 3;
|
|
75
|
+
declare const DEFAULT_TOKEN_REFRESH_SKEW_MS = 60000;
|
|
23
76
|
|
|
24
77
|
interface HttpRequestOptions {
|
|
25
78
|
method: string;
|
|
@@ -39,9 +92,25 @@ declare class HttpClient {
|
|
|
39
92
|
private readonly timeout;
|
|
40
93
|
private readonly retryOnRateLimit;
|
|
41
94
|
private readonly maxRetries;
|
|
42
|
-
|
|
95
|
+
private readonly oauth;
|
|
96
|
+
/** Single-flight guard so concurrent requests share one refresh. */
|
|
97
|
+
private refreshInFlight;
|
|
98
|
+
constructor(config: PlanesConfig);
|
|
43
99
|
request<T>(options: HttpRequestOptions): Promise<T>;
|
|
44
100
|
getAuthHeaders(): Record<string, string>;
|
|
101
|
+
/**
|
|
102
|
+
* Resolve the Authorization header for the current request. For the static
|
|
103
|
+
* accessToken/apiKey modes this is {@link getAuthHeaders}. For the
|
|
104
|
+
* OAuth-managed path it reads the stored token, proactively refreshing when
|
|
105
|
+
* the token is within the skew window of expiry.
|
|
106
|
+
*/
|
|
107
|
+
private resolveAuthHeaders;
|
|
108
|
+
/**
|
|
109
|
+
* Refresh the OAuth access token via `refreshAccessToken` and persist the new
|
|
110
|
+
* tokens through the configured {@link TokenStorage}. Concurrent callers share
|
|
111
|
+
* a single in-flight refresh.
|
|
112
|
+
*/
|
|
113
|
+
private refreshTokens;
|
|
45
114
|
private buildUrl;
|
|
46
115
|
private parseRetryAfter;
|
|
47
116
|
private mapError;
|
|
@@ -151,15 +220,397 @@ declare class InventoryResource extends BaseResource {
|
|
|
151
220
|
}
|
|
152
221
|
|
|
153
222
|
/**
|
|
154
|
-
*
|
|
223
|
+
* Wire shapes for the Contacts + Contact Channels API.
|
|
224
|
+
*
|
|
225
|
+
* Mirrors `createContactSchema`/`updateContactSchema` and
|
|
226
|
+
* `createContactChannelSchema`/`updateContactChannelSchema` in
|
|
227
|
+
* `packages/shared/src/validation/contact.ts`, plus the actual Drizzle-row
|
|
228
|
+
* response shapes returned by `ContactsService` / `ContactChannelsService`.
|
|
229
|
+
* Types are redefined here (not imported) so the published package carries no
|
|
230
|
+
* runtime or type dependency on `@blueprint/shared`. Internal-only columns
|
|
231
|
+
* (tenantId, deletedAt, createdBy, …) are intentionally omitted — see
|
|
232
|
+
* `CustomerDto` for the house style.
|
|
233
|
+
*/
|
|
234
|
+
type ContactSource = 'manual' | 'import' | 'convert_from_customer';
|
|
235
|
+
interface ContactDto {
|
|
236
|
+
id: string;
|
|
237
|
+
firstName: string;
|
|
238
|
+
lastName: string | null;
|
|
239
|
+
notes: string | null;
|
|
240
|
+
customFields: Record<string, unknown> | null;
|
|
241
|
+
source: ContactSource | null;
|
|
242
|
+
createdAt: string;
|
|
243
|
+
updatedAt: string;
|
|
244
|
+
}
|
|
245
|
+
interface CreateContactDto {
|
|
246
|
+
firstName: string;
|
|
247
|
+
lastName?: string | null;
|
|
248
|
+
notes?: string | null;
|
|
249
|
+
customFields?: Record<string, unknown> | null;
|
|
250
|
+
source?: ContactSource | null;
|
|
251
|
+
}
|
|
252
|
+
interface UpdateContactDto {
|
|
253
|
+
firstName?: string;
|
|
254
|
+
lastName?: string | null;
|
|
255
|
+
notes?: string | null;
|
|
256
|
+
customFields?: Record<string, unknown> | null;
|
|
257
|
+
source?: ContactSource | null;
|
|
258
|
+
}
|
|
259
|
+
type ContactChannelType = 'email' | 'phone' | 'url';
|
|
260
|
+
type ContactChannelPhoneType = 'mobile' | 'landline' | 'voip' | 'unknown';
|
|
261
|
+
interface ContactChannelDto {
|
|
262
|
+
id: string;
|
|
263
|
+
contactId: string;
|
|
264
|
+
channelType: ContactChannelType;
|
|
265
|
+
label: string | null;
|
|
266
|
+
value: string;
|
|
267
|
+
isPrimary: boolean;
|
|
268
|
+
phoneType: ContactChannelPhoneType | null;
|
|
269
|
+
smsCapable: boolean | null;
|
|
270
|
+
dnc: boolean | null;
|
|
271
|
+
marketingOptOut: boolean | null;
|
|
272
|
+
createdAt: string;
|
|
273
|
+
updatedAt: string;
|
|
274
|
+
}
|
|
275
|
+
interface CreateContactChannelDto {
|
|
276
|
+
channelType: ContactChannelType;
|
|
277
|
+
label?: string | null;
|
|
278
|
+
value: string;
|
|
279
|
+
isPrimary?: boolean;
|
|
280
|
+
dnc?: boolean | null;
|
|
281
|
+
marketingOptOut?: boolean | null;
|
|
282
|
+
}
|
|
283
|
+
interface UpdateContactChannelDto {
|
|
284
|
+
label?: string | null;
|
|
285
|
+
value?: string;
|
|
286
|
+
isPrimary?: boolean;
|
|
287
|
+
dnc?: boolean | null;
|
|
288
|
+
marketingOptOut?: boolean | null;
|
|
289
|
+
}
|
|
290
|
+
/** A channel elsewhere in the tenant with the same normalized value. */
|
|
291
|
+
interface ContactChannelDuplicateMatch {
|
|
292
|
+
contactId: string;
|
|
293
|
+
channelId: string;
|
|
294
|
+
value: string;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* `POST /contacts/:id/channels` return shape — `ContactChannelsService.addChannel()`
|
|
298
|
+
* always returns this wrapper, never a flat channel. `duplicateOf` is the
|
|
299
|
+
* dedup signal (empty array when there's no match) — the point of this API
|
|
300
|
+
* for connectors doing identity resolution (e.g. Messenger).
|
|
301
|
+
*/
|
|
302
|
+
interface AddContactChannelResult {
|
|
303
|
+
channel: ContactChannelDto;
|
|
304
|
+
duplicateOf: ContactChannelDuplicateMatch[];
|
|
305
|
+
}
|
|
306
|
+
interface ContactMergeResult {
|
|
307
|
+
winnerId: string;
|
|
308
|
+
loserId: string;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* `/contacts/:id/channels` — POST/PATCH/DELETE only. There is no list/get
|
|
313
|
+
* route: reads come embedded in the contact detail response (`GET
|
|
314
|
+
* /contacts/:id` returns `{ ...contact, channels, links, mergedInto }`), so
|
|
315
|
+
* this does NOT extend `BaseResource`.
|
|
316
|
+
*/
|
|
317
|
+
declare class ContactChannelResource {
|
|
318
|
+
private readonly http;
|
|
319
|
+
private readonly basePath;
|
|
320
|
+
constructor(http: HttpClient, contactId: string);
|
|
321
|
+
/** Returns `{ channel, duplicateOf }` — `duplicateOf` is the dedup signal. */
|
|
322
|
+
create(data: CreateContactChannelDto): Promise<AddContactChannelResult>;
|
|
323
|
+
update(channelId: string, data: UpdateContactChannelDto): Promise<ContactChannelDto>;
|
|
324
|
+
delete(channelId: string): Promise<void>;
|
|
325
|
+
}
|
|
326
|
+
declare class ContactResource extends BaseResource<ContactDto, CreateContactDto, UpdateContactDto> {
|
|
327
|
+
constructor(http: ConstructorParameters<typeof BaseResource>[0]);
|
|
328
|
+
merge(winnerId: string, loserId: string): Promise<ContactMergeResult>;
|
|
329
|
+
channels(contactId: string): ContactChannelResource;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Wire shapes for the generic activity/timeline API (E31-F1).
|
|
334
|
+
*
|
|
335
|
+
* Mirrors `recordActivitySchema` (write) and `ActivityRecord` (read) in
|
|
336
|
+
* `packages/shared/src/validation/activity.ts` — both snake_case on the wire.
|
|
337
|
+
* Types are redefined here (not imported) so the published package carries no
|
|
338
|
+
* runtime or type dependency on `@blueprint/shared`.
|
|
339
|
+
*/
|
|
340
|
+
interface CreateActivityDto {
|
|
341
|
+
entity_type: string;
|
|
342
|
+
entity_id?: string | null;
|
|
343
|
+
/** The contact a channel-targeted send was addressed to, if any. */
|
|
344
|
+
contact_id?: string | null;
|
|
345
|
+
source: string;
|
|
346
|
+
type: string;
|
|
347
|
+
occurred_at?: string;
|
|
348
|
+
actor?: string | null;
|
|
349
|
+
/** Required — a short human-readable description of the event. */
|
|
350
|
+
summary: string;
|
|
351
|
+
external_ref?: string | null;
|
|
352
|
+
match_key?: string | null;
|
|
353
|
+
payload?: Record<string, unknown>;
|
|
354
|
+
payload_version?: number;
|
|
355
|
+
}
|
|
356
|
+
interface ActivityDto {
|
|
357
|
+
id: string;
|
|
358
|
+
type: string;
|
|
359
|
+
source: string;
|
|
360
|
+
occurred_at: string;
|
|
361
|
+
actor: string | null;
|
|
362
|
+
summary: string;
|
|
363
|
+
entity_type: string;
|
|
364
|
+
entity_id: string | null;
|
|
365
|
+
contact_id: string | null;
|
|
366
|
+
payload: Record<string, unknown>;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
declare class ActivityResource {
|
|
370
|
+
private readonly http;
|
|
371
|
+
constructor(http: HttpClient);
|
|
372
|
+
create(data: CreateActivityDto): Promise<ActivityDto>;
|
|
373
|
+
list(params?: ListParams): Promise<PaginatedResponse<ActivityDto>>;
|
|
374
|
+
listForEntity(entityType: string, entityId: string, params?: ListParams): Promise<PaginatedResponse<ActivityDto>>;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Wire shapes for the Tags & entity-tagging API.
|
|
379
|
+
*
|
|
380
|
+
* Mirrors `createTagSchema`/`updateTagSchema`/`setEntityTagsSchema` in
|
|
381
|
+
* `packages/shared/src/validation/tags.ts`, plus `TagsService`'s actual
|
|
382
|
+
* response shapes — snake_case, per `toDto()`/`list()`. Types are redefined
|
|
383
|
+
* here (not imported) so the published package carries no runtime or type
|
|
384
|
+
* dependency on `@blueprint/shared`.
|
|
385
|
+
*/
|
|
386
|
+
interface TagDto {
|
|
387
|
+
id: string;
|
|
388
|
+
name: string;
|
|
389
|
+
color: string | null;
|
|
390
|
+
created_at: string;
|
|
391
|
+
updated_at: string;
|
|
392
|
+
/** Only present on `list()` — `create()`/`update()` omit it. */
|
|
393
|
+
usage_count?: number;
|
|
394
|
+
}
|
|
395
|
+
interface CreateTagDto {
|
|
396
|
+
name: string;
|
|
397
|
+
color?: string | null;
|
|
398
|
+
}
|
|
399
|
+
interface UpdateTagDto {
|
|
400
|
+
name?: string;
|
|
401
|
+
color?: string | null;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Declarative set-tags input. Provide `tag_ids` and/or `tag_names` — at least
|
|
405
|
+
* one must be present (the server rejects neither); an empty array clears
|
|
406
|
+
* that side of the set.
|
|
407
|
+
*/
|
|
408
|
+
interface SetEntityTagsDto {
|
|
409
|
+
tag_ids?: string[];
|
|
410
|
+
tag_names?: string[];
|
|
411
|
+
}
|
|
412
|
+
interface EntityTagDto {
|
|
413
|
+
id: string;
|
|
414
|
+
name: string;
|
|
415
|
+
color: string | null;
|
|
416
|
+
}
|
|
417
|
+
interface TagEntityDto {
|
|
418
|
+
entity_type: string;
|
|
419
|
+
entity_id: string;
|
|
420
|
+
}
|
|
421
|
+
interface TagEntitiesPage {
|
|
422
|
+
data: TagEntityDto[];
|
|
423
|
+
page: number;
|
|
424
|
+
limit: number;
|
|
425
|
+
total: number;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
declare class TagResource extends BaseResource<TagDto, CreateTagDto, UpdateTagDto> {
|
|
429
|
+
constructor(http: ConstructorParameters<typeof BaseResource>[0]);
|
|
430
|
+
/** `TagsService.setEntityTags()` returns the final tag set wrapped in `{ tags }`. */
|
|
431
|
+
setEntityTags(entityType: string, entityId: string, data: SetEntityTagsDto): Promise<{
|
|
432
|
+
tags: TagDto[];
|
|
433
|
+
}>;
|
|
434
|
+
getEntityTags(entityType: string, entityId: string): Promise<EntityTagDto[]>;
|
|
435
|
+
getEntities(tagId: string, params?: Record<string, unknown>): Promise<TagEntitiesPage>;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* The kinds of change a webhook event can describe.
|
|
440
|
+
*
|
|
441
|
+
* Mirrored (self-contained, NOT imported) from
|
|
442
|
+
* `packages/shared/src/types/entity-events.ts` — the SDK must not depend on
|
|
443
|
+
* `@blueprint/shared`. Keep this union byte-identical to that source of truth.
|
|
444
|
+
*/
|
|
445
|
+
type EntityEventType = 'created' | 'updated' | 'deleted' | 'status_changed' | 'entity.tagged' | 'entity.untagged' | 'list.member_added' | 'list.member_removed' | 'entity.assigned' | 'entity.unassigned' | 'team.member_added' | 'team.member_removed' | 'task.created' | 'task.updated' | 'task.completed' | 'task.cancelled' | 'comment.created';
|
|
446
|
+
/**
|
|
447
|
+
* The entity kinds a webhook event can target.
|
|
448
|
+
*
|
|
449
|
+
* Mirrored (self-contained, NOT imported) from
|
|
450
|
+
* `packages/shared/src/types/entity-events.ts`. Keep byte-identical.
|
|
451
|
+
*/
|
|
452
|
+
type EntityType = 'customer' | 'opportunity' | 'job' | 'estimate' | 'contract' | 'property' | 'inventory_item' | 'agent_execution' | 'calendar_event' | 'invoice' | 'payment';
|
|
453
|
+
/**
|
|
454
|
+
* A single entity-change event as emitted onto the Planes event bus and
|
|
455
|
+
* delivered inside a {@link WebhookDelivery}.
|
|
456
|
+
*
|
|
457
|
+
* Mirrored from `EntityEvent` in
|
|
458
|
+
* `packages/shared/src/types/entity-events.ts`.
|
|
459
|
+
*/
|
|
460
|
+
interface EntityEvent {
|
|
461
|
+
/** What happened (e.g. 'created', 'status_changed'). */
|
|
462
|
+
event_type: EntityEventType;
|
|
463
|
+
/** The kind of record this event is about. */
|
|
464
|
+
entity_type: EntityType;
|
|
465
|
+
/** The affected record's ID. */
|
|
466
|
+
entity_id: string;
|
|
467
|
+
/** Tenant ID that owns this event. */
|
|
468
|
+
tenant_id: string;
|
|
469
|
+
/** Per-field before/after values for the change. */
|
|
470
|
+
changes: Record<string, {
|
|
471
|
+
old: unknown;
|
|
472
|
+
new: unknown;
|
|
473
|
+
}>;
|
|
474
|
+
/** Full record snapshot at the time of the event. */
|
|
475
|
+
snapshot: Record<string, unknown>;
|
|
476
|
+
/** ISO 8601 timestamp of when the event occurred. */
|
|
477
|
+
timestamp: string;
|
|
478
|
+
/** Correlation ID linking related events/actions. */
|
|
479
|
+
correlation_id: string;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* The webhook delivery envelope Planes POSTs to a subscriber's URL.
|
|
483
|
+
*
|
|
484
|
+
* This is the outermost body: a delivery ID, the schema `version`, the wrapped
|
|
485
|
+
* {@link EntityEvent}, and the subscription that matched. The entity body lives
|
|
486
|
+
* at `event.snapshot` — there is no top-level `data`.
|
|
487
|
+
*/
|
|
488
|
+
interface WebhookDelivery {
|
|
489
|
+
/** Delivery ID (the delivery-log row id). */
|
|
490
|
+
id: string;
|
|
491
|
+
/**
|
|
492
|
+
* Webhook payload schema version (from the `X-Planes-Webhook-Version`
|
|
493
|
+
* header / envelope, emitted API-side per E39-F1-S2). v1 requires no
|
|
494
|
+
* branching; surfaced here so consumers can switch on it in the future.
|
|
495
|
+
*/
|
|
496
|
+
version: number;
|
|
497
|
+
/** The entity-change event that triggered this delivery. */
|
|
498
|
+
event: EntityEvent;
|
|
499
|
+
/** ID of the webhook subscription that matched and produced this delivery. */
|
|
500
|
+
subscription_id: string;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* Verify an HMAC-SHA256 webhook signature using timing-safe comparison.
|
|
504
|
+
*
|
|
505
|
+
* The signature header format is: `sha256=<hex-digest>`
|
|
506
|
+
*
|
|
507
|
+
* @param payload - The raw request body (string or Buffer)
|
|
508
|
+
* @param signature - The X-Planes-Signature header value
|
|
509
|
+
* @param secret - The webhook signing secret
|
|
510
|
+
* @returns true if the signature is valid, false otherwise
|
|
511
|
+
*/
|
|
512
|
+
declare function verifyWebhookSignature(payload: string | Buffer, signature: string, secret: string): boolean;
|
|
513
|
+
/**
|
|
514
|
+
* Parse and verify a webhook delivery payload.
|
|
515
|
+
*
|
|
516
|
+
* @param body - The raw request body as a string
|
|
517
|
+
* @param signature - The X-Planes-Signature header value
|
|
518
|
+
* @param secret - The webhook signing secret
|
|
519
|
+
* @returns The parsed and verified {@link WebhookDelivery} envelope
|
|
520
|
+
* @throws WebhookVerificationError if signature is invalid
|
|
521
|
+
*/
|
|
522
|
+
declare function parseWebhookEvent(body: string, signature: string, secret: string): WebhookDelivery;
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* A handler invoked for a matching webhook delivery. May be sync or async;
|
|
526
|
+
* async handlers are awaited by {@link WebhookRouter.process}.
|
|
527
|
+
*/
|
|
528
|
+
type WebhookHandler = (delivery: WebhookDelivery) => void | Promise<void>;
|
|
529
|
+
/** Input to {@link WebhookRouter.process} — the raw request pieces. */
|
|
530
|
+
interface WebhookProcessInput {
|
|
531
|
+
/** The raw request body as a string (verbatim — not re-serialized JSON). */
|
|
532
|
+
body: string;
|
|
533
|
+
/** The `X-Planes-Signature` header value (`sha256=<hex>`). */
|
|
534
|
+
signature: string;
|
|
535
|
+
/** The webhook signing secret for this subscription. */
|
|
536
|
+
secret: string;
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Ergonomic event router over the corrected {@link WebhookDelivery} envelope.
|
|
540
|
+
*
|
|
541
|
+
* The platform models an event as two fields — `event.entity_type` and
|
|
542
|
+
* `event.event_type` — which this router joins into a dotted composite key
|
|
543
|
+
* `` `${entity_type}.${event_type}` `` (e.g. `opportunity.created`). Handlers
|
|
544
|
+
* subscribe to that key, to an entity-scoped wildcard `` `${entity_type}.*` ``
|
|
545
|
+
* (e.g. `opportunity.*`), or to the global wildcard `*`.
|
|
546
|
+
*
|
|
547
|
+
* The router is a pure dispatcher: it verifies + parses the delivery, then
|
|
548
|
+
* fans it out to matching handlers. It holds no connection and no auth — the
|
|
549
|
+
* signing secret is supplied per {@link process} call — so the same client's
|
|
550
|
+
* `webhooks` router works across subscriptions. Wire it into your own HTTP
|
|
551
|
+
* handler.
|
|
155
552
|
*
|
|
156
553
|
* @example
|
|
157
554
|
* ```ts
|
|
158
|
-
*
|
|
555
|
+
* client.webhooks
|
|
556
|
+
* .on('opportunity.created', (d) => console.log(d.event.snapshot))
|
|
557
|
+
* .on('opportunity.*', (d) => audit(d))
|
|
558
|
+
* .on('*', (d) => metrics.count(d.event.entity_type));
|
|
559
|
+
*
|
|
560
|
+
* // inside your POST handler:
|
|
561
|
+
* await client.webhooks.process({ body: rawBody, signature, secret });
|
|
562
|
+
* ```
|
|
563
|
+
*
|
|
564
|
+
* ### Dotted `event_type` composite keys
|
|
565
|
+
*
|
|
566
|
+
* Four event types are themselves dotted — `entity.tagged`, `entity.untagged`,
|
|
567
|
+
* `list.member_added`, `list.member_removed`. Joined with their entity these
|
|
568
|
+
* yield keys with more than one dot, e.g. `customer.entity.tagged`. This router
|
|
569
|
+
* matches on the literal `` `${entity_type}.${event_type}` `` string, so
|
|
570
|
+
* subscribe with the full key (`'customer.entity.tagged'`). The entity wildcard
|
|
571
|
+
* (`'customer.*'`) and global wildcard (`'*'`) still match these deliveries;
|
|
572
|
+
* only mid-segment wildcards like `'customer.entity.*'` are intentionally
|
|
573
|
+
* unsupported.
|
|
574
|
+
*/
|
|
575
|
+
declare class WebhookRouter {
|
|
576
|
+
/** pattern (composite key / entity wildcard / '*') → registered handlers. */
|
|
577
|
+
private readonly handlers;
|
|
578
|
+
/**
|
|
579
|
+
* Register a handler for an event pattern.
|
|
580
|
+
*
|
|
581
|
+
* @param pattern - `"{entity_type}.{event_type}"` (e.g. `opportunity.created`),
|
|
582
|
+
* an entity wildcard `"{entity_type}.*"` (e.g. `opportunity.*`), or the
|
|
583
|
+
* global wildcard `"*"`. Matching is exact-string against the derived key;
|
|
584
|
+
* see the class docs for the dotted-`event_type` shape.
|
|
585
|
+
* @param handler - Invoked with the full {@link WebhookDelivery} on a match.
|
|
586
|
+
* @returns `this`, for chaining.
|
|
587
|
+
*/
|
|
588
|
+
on(pattern: string, handler: WebhookHandler): this;
|
|
589
|
+
/**
|
|
590
|
+
* Verify, parse, and dispatch a webhook delivery.
|
|
591
|
+
*
|
|
592
|
+
* The signature is verified first: on failure this throws
|
|
593
|
+
* {@link WebhookVerificationError} and dispatches nothing. On success it
|
|
594
|
+
* dispatches to every matching handler — specific key, then entity wildcard,
|
|
595
|
+
* then global — awaiting async handlers before resolving.
|
|
596
|
+
*
|
|
597
|
+
* @param input - The raw `{ body, signature, secret }`.
|
|
598
|
+
* @returns The parsed {@link WebhookDelivery}.
|
|
599
|
+
* @throws WebhookVerificationError if the signature is invalid.
|
|
600
|
+
*/
|
|
601
|
+
process(input: WebhookProcessInput): Promise<WebhookDelivery>;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* PlanesClient — typed API client for the Planes CRM platform.
|
|
606
|
+
*
|
|
607
|
+
* @example
|
|
608
|
+
* ```ts
|
|
609
|
+
* const client = new PlanesClient({ accessToken: 'tok_...' });
|
|
159
610
|
* const opps = await client.opportunities.list({ page: 1, limit: 20 });
|
|
160
611
|
* ```
|
|
161
612
|
*/
|
|
162
|
-
declare class
|
|
613
|
+
declare class PlanesClient {
|
|
163
614
|
private readonly http;
|
|
164
615
|
private _opportunities?;
|
|
165
616
|
private _customers?;
|
|
@@ -170,7 +621,11 @@ declare class BlueprintClient {
|
|
|
170
621
|
private _estimates?;
|
|
171
622
|
private _invoices?;
|
|
172
623
|
private _inventory?;
|
|
173
|
-
|
|
624
|
+
private _contacts?;
|
|
625
|
+
private _activities?;
|
|
626
|
+
private _tags?;
|
|
627
|
+
private _webhooks?;
|
|
628
|
+
constructor(config: PlanesConfig);
|
|
174
629
|
get opportunities(): OpportunityResource;
|
|
175
630
|
get customers(): CustomerResource;
|
|
176
631
|
get properties(): PropertyResource;
|
|
@@ -180,12 +635,22 @@ declare class BlueprintClient {
|
|
|
180
635
|
get estimates(): EstimateResource;
|
|
181
636
|
get invoices(): InvoiceResource;
|
|
182
637
|
get inventory(): InventoryResource;
|
|
638
|
+
get contacts(): ContactResource;
|
|
639
|
+
get activities(): ActivityResource;
|
|
640
|
+
get tags(): TagResource;
|
|
641
|
+
/**
|
|
642
|
+
* Ergonomic webhook event router — `client.webhooks.on(pattern, handler)`
|
|
643
|
+
* plus `client.webhooks.process({ body, signature, secret })`. Auth-independent
|
|
644
|
+
* (the signing secret is supplied per `.process()` call), so it is created
|
|
645
|
+
* lazily without the HTTP client. See {@link WebhookRouter}.
|
|
646
|
+
*/
|
|
647
|
+
get webhooks(): WebhookRouter;
|
|
183
648
|
}
|
|
184
649
|
|
|
185
650
|
/**
|
|
186
651
|
* Base error class for all Planes SDK errors.
|
|
187
652
|
*/
|
|
188
|
-
declare class
|
|
653
|
+
declare class PlanesError extends Error {
|
|
189
654
|
readonly status: number;
|
|
190
655
|
readonly code: string;
|
|
191
656
|
constructor(message: string, status: number, code: string);
|
|
@@ -193,19 +658,19 @@ declare class BlueprintError extends Error {
|
|
|
193
658
|
/**
|
|
194
659
|
* Thrown when authentication fails (401).
|
|
195
660
|
*/
|
|
196
|
-
declare class AuthenticationError extends
|
|
661
|
+
declare class AuthenticationError extends PlanesError {
|
|
197
662
|
constructor(message?: string);
|
|
198
663
|
}
|
|
199
664
|
/**
|
|
200
665
|
* Thrown when the user lacks permission (403).
|
|
201
666
|
*/
|
|
202
|
-
declare class AuthorizationError extends
|
|
667
|
+
declare class AuthorizationError extends PlanesError {
|
|
203
668
|
constructor(message?: string);
|
|
204
669
|
}
|
|
205
670
|
/**
|
|
206
671
|
* Thrown when a resource is not found (404).
|
|
207
672
|
*/
|
|
208
|
-
declare class NotFoundError extends
|
|
673
|
+
declare class NotFoundError extends PlanesError {
|
|
209
674
|
constructor(message?: string);
|
|
210
675
|
}
|
|
211
676
|
/** Field-level validation error detail. */
|
|
@@ -217,33 +682,33 @@ interface FieldError {
|
|
|
217
682
|
/**
|
|
218
683
|
* Thrown for validation failures (400) with field-level error details.
|
|
219
684
|
*/
|
|
220
|
-
declare class ValidationError extends
|
|
685
|
+
declare class ValidationError extends PlanesError {
|
|
221
686
|
readonly fieldErrors: FieldError[];
|
|
222
687
|
constructor(message?: string, fieldErrors?: FieldError[]);
|
|
223
688
|
}
|
|
224
689
|
/**
|
|
225
690
|
* Thrown when rate limited (429). Includes retryAfter in seconds.
|
|
226
691
|
*/
|
|
227
|
-
declare class RateLimitError extends
|
|
692
|
+
declare class RateLimitError extends PlanesError {
|
|
228
693
|
readonly retryAfter: number;
|
|
229
694
|
constructor(retryAfter?: number, message?: string);
|
|
230
695
|
}
|
|
231
696
|
/**
|
|
232
697
|
* Thrown for server errors (500+).
|
|
233
698
|
*/
|
|
234
|
-
declare class ServerError extends
|
|
699
|
+
declare class ServerError extends PlanesError {
|
|
235
700
|
constructor(message?: string);
|
|
236
701
|
}
|
|
237
702
|
/**
|
|
238
703
|
* Thrown when webhook signature verification fails.
|
|
239
704
|
*/
|
|
240
|
-
declare class WebhookVerificationError extends
|
|
705
|
+
declare class WebhookVerificationError extends PlanesError {
|
|
241
706
|
constructor(message?: string);
|
|
242
707
|
}
|
|
243
708
|
/**
|
|
244
709
|
* Thrown for OAuth flow errors.
|
|
245
710
|
*/
|
|
246
|
-
declare class OAuthError extends
|
|
711
|
+
declare class OAuthError extends PlanesError {
|
|
247
712
|
readonly errorCode: string;
|
|
248
713
|
constructor(errorCode: string, message?: string);
|
|
249
714
|
}
|
|
@@ -251,54 +716,130 @@ declare class OAuthError extends BlueprintError {
|
|
|
251
716
|
/**
|
|
252
717
|
* ActionHandler, ActionContext, and ActionResult types for the SDK.
|
|
253
718
|
*
|
|
254
|
-
* These
|
|
255
|
-
*
|
|
256
|
-
*
|
|
719
|
+
* These model the REAL over-the-wire contract that the Planes platform's
|
|
720
|
+
* external-agent dispatcher POSTs to a registered agent endpoint
|
|
721
|
+
* (`apps/api/src/modules/marketplace/handlers/external-agent.handler.ts`).
|
|
722
|
+
* They are self-contained — the SDK never imports `@blueprint/shared`, so
|
|
723
|
+
* external developers can depend on this package alone.
|
|
724
|
+
*
|
|
725
|
+
* ## Wire contract (source of truth: external-agent.handler.ts)
|
|
726
|
+
*
|
|
727
|
+
* The platform POSTs a FLATTENED, WRAPPED envelope — NOT the platform's internal
|
|
728
|
+
* nested `ActionContext`:
|
|
729
|
+
*
|
|
730
|
+
* ```json
|
|
731
|
+
* {
|
|
732
|
+
* "action_context": {
|
|
733
|
+
* "action_id": "act-1",
|
|
734
|
+
* "action_type": "enrich-opportunity",
|
|
735
|
+
* "entity_type": "opportunity",
|
|
736
|
+
* "entity_id": "opp-1",
|
|
737
|
+
* "tenant_id": "tenant-1",
|
|
738
|
+
* "data": { "...": "entity snapshot" },
|
|
739
|
+
* "stage": { "id": "...", "name": "...", "rules": null, "agent_role": null, "agent_instructions": null } | null,
|
|
740
|
+
* "metadata": { "trigger": { ... }, "execution": { ... } }
|
|
741
|
+
* },
|
|
742
|
+
* "callback_token": "jwt.callback.token",
|
|
743
|
+
* "callback_base_url": "https://…/api/v1"
|
|
744
|
+
* }
|
|
745
|
+
* ```
|
|
746
|
+
*
|
|
747
|
+
* ### Field convention
|
|
748
|
+
* The SDK mirrors the wire's **snake_case** verbatim (no camelCase translation),
|
|
749
|
+
* so a parsed request body maps 1:1 onto {@link ActionContext}. The one shaping
|
|
750
|
+
* the SDK server performs is to LIFT `callback_token` / `callback_base_url` (and
|
|
751
|
+
* the `X-Planes-Request-Id` header as {@link ActionContext.request_id}) up onto
|
|
752
|
+
* the same object handed to the handler — so a handler receives one flat context.
|
|
753
|
+
*
|
|
754
|
+
* ### Result delivery
|
|
755
|
+
* The platform reads the handler's {@link ActionResult} from the **synchronous
|
|
756
|
+
* HTTP response body** (`await response.json()` in the handler, dispatching on
|
|
757
|
+
* `status`). There is currently no async callback endpoint — `callback_token` /
|
|
758
|
+
* `callback_base_url` are minted for the agent to call back INTO the Planes API,
|
|
759
|
+
* not for the platform to poll. Handlers therefore just `return` their result.
|
|
760
|
+
*
|
|
761
|
+
* ### Not on the wire (intentionally absent vs. the platform's internal type)
|
|
762
|
+
* `permissions`, `agent_installation_id`, and `action.config` are NOT sent to
|
|
763
|
+
* external agents, so they are not modeled here.
|
|
257
764
|
*/
|
|
258
765
|
/** Trigger types for action execution. */
|
|
259
766
|
type TriggerType = 'workflow_transition' | 'schedule' | 'event' | 'agent';
|
|
260
|
-
/**
|
|
767
|
+
/**
|
|
768
|
+
* Permission levels (1 = most restricted, 4 = most powerful).
|
|
769
|
+
*
|
|
770
|
+
* Not part of the external wire envelope (the platform does not send permission
|
|
771
|
+
* metadata to external agents); retained as a public type for callers that
|
|
772
|
+
* reason about permission levels elsewhere.
|
|
773
|
+
*/
|
|
261
774
|
type PermissionLevel = 1 | 2 | 3 | 4;
|
|
775
|
+
/** How this action was triggered (wire: `action_context.metadata.trigger`). */
|
|
776
|
+
interface ActionTrigger {
|
|
777
|
+
type: TriggerType;
|
|
778
|
+
source_id: string;
|
|
779
|
+
triggered_by: string;
|
|
780
|
+
}
|
|
781
|
+
/** Execution/correlation metadata (wire: `action_context.metadata.execution`). */
|
|
782
|
+
interface ActionExecution {
|
|
783
|
+
id: string;
|
|
784
|
+
correlation_id: string;
|
|
785
|
+
attempt: number;
|
|
786
|
+
timeout_ms: number;
|
|
787
|
+
}
|
|
788
|
+
/** Trigger + execution metadata (wire: `action_context.metadata`). */
|
|
789
|
+
interface ActionMetadata {
|
|
790
|
+
trigger: ActionTrigger;
|
|
791
|
+
execution: ActionExecution;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Destination stage metadata — present for `workflow_transition` triggers,
|
|
795
|
+
* `null` otherwise (wire: `action_context.stage`).
|
|
796
|
+
*/
|
|
797
|
+
interface ActionStage {
|
|
798
|
+
id: string;
|
|
799
|
+
name: string;
|
|
800
|
+
rules: string | null;
|
|
801
|
+
agent_role: string | null;
|
|
802
|
+
agent_instructions: string | null;
|
|
803
|
+
}
|
|
262
804
|
/**
|
|
263
805
|
* ActionContext envelope — passed to handlers on each invocation.
|
|
806
|
+
*
|
|
807
|
+
* Shape matches the platform's flattened `action_context` (see file header),
|
|
808
|
+
* with `callback_token`, `callback_base_url`, and `request_id` lifted onto the
|
|
809
|
+
* same object by the SDK server for handler convenience.
|
|
264
810
|
*/
|
|
265
811
|
interface ActionContext {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
id: string;
|
|
298
|
-
correlation_id: string;
|
|
299
|
-
attempt: number;
|
|
300
|
-
timeout_ms: number;
|
|
301
|
-
};
|
|
812
|
+
/** Unique ID of the action being executed (wire: `action_context.action_id`). */
|
|
813
|
+
action_id: string;
|
|
814
|
+
/** The action type — the SDK routes to a handler by this value. */
|
|
815
|
+
action_type: string;
|
|
816
|
+
/** The entity type this action operates on, e.g. `'opportunity'`. */
|
|
817
|
+
entity_type: string;
|
|
818
|
+
/** The entity's ID. */
|
|
819
|
+
entity_id: string;
|
|
820
|
+
/** The tenant (company) ID this action runs for. */
|
|
821
|
+
tenant_id: string;
|
|
822
|
+
/** The entity snapshot captured at trigger time (wire: `action_context.data`). */
|
|
823
|
+
data: Record<string, unknown>;
|
|
824
|
+
/** Destination stage metadata for workflow transitions; `null` otherwise. */
|
|
825
|
+
stage: ActionStage | null;
|
|
826
|
+
/** Trigger + execution metadata. */
|
|
827
|
+
metadata: ActionMetadata;
|
|
828
|
+
/**
|
|
829
|
+
* Short-lived (5-min) JWT for calling back INTO the Planes API at
|
|
830
|
+
* {@link callback_base_url}. Lifted from the envelope's top-level `callback_token`.
|
|
831
|
+
*/
|
|
832
|
+
callback_token: string;
|
|
833
|
+
/**
|
|
834
|
+
* Base URL (e.g. `https://…/api/v1`) for authenticated callbacks using
|
|
835
|
+
* {@link callback_token}. Lifted from the envelope's top-level `callback_base_url`.
|
|
836
|
+
*/
|
|
837
|
+
callback_base_url: string;
|
|
838
|
+
/**
|
|
839
|
+
* Per-invocation correlation ID from the `X-Planes-Request-Id` request header,
|
|
840
|
+
* for logging/idempotency. `undefined` if the header was absent.
|
|
841
|
+
*/
|
|
842
|
+
request_id?: string;
|
|
302
843
|
}
|
|
303
844
|
/** A follow-up action request (used with delegated status). */
|
|
304
845
|
interface ActionRequest {
|
|
@@ -308,6 +849,10 @@ interface ActionRequest {
|
|
|
308
849
|
}
|
|
309
850
|
/**
|
|
310
851
|
* ActionResult — the return value from a handler's execute() method.
|
|
852
|
+
*
|
|
853
|
+
* The platform reads this from the synchronous HTTP response body and dispatches
|
|
854
|
+
* on `status` (`completed` → `output`, `failed` → `error`/`retryable`,
|
|
855
|
+
* `needs_human` → `reason`/`context`, `delegated` → `follow_up_actions`).
|
|
311
856
|
*/
|
|
312
857
|
type ActionResult = {
|
|
313
858
|
status: 'completed';
|
|
@@ -332,14 +877,15 @@ interface ActionHandler {
|
|
|
332
877
|
execute(ctx: ActionContext): Promise<ActionResult>;
|
|
333
878
|
}
|
|
334
879
|
/**
|
|
335
|
-
* Generic typed version of ActionHandler for specific
|
|
880
|
+
* Generic typed version of ActionHandler for a specific entity-data/output shape.
|
|
881
|
+
*
|
|
882
|
+
* `TData` narrows {@link ActionContext.data} (the entity snapshot) — the external
|
|
883
|
+
* wire carries no per-action `config`, so typed handler input is the entity data.
|
|
336
884
|
*/
|
|
337
|
-
interface AgentHandler<
|
|
885
|
+
interface AgentHandler<TData = Record<string, unknown>, TOutput = unknown> {
|
|
338
886
|
readonly type: string;
|
|
339
887
|
execute(ctx: ActionContext & {
|
|
340
|
-
|
|
341
|
-
config: TInput;
|
|
342
|
-
};
|
|
888
|
+
data: TData;
|
|
343
889
|
}): Promise<{
|
|
344
890
|
status: 'completed';
|
|
345
891
|
output?: TOutput;
|
|
@@ -362,7 +908,7 @@ interface AgentHandler<TInput = Record<string, unknown>, TOutput = unknown> {
|
|
|
362
908
|
type HandlerMap = Record<string, ActionHandler>;
|
|
363
909
|
|
|
364
910
|
/**
|
|
365
|
-
* Options for
|
|
911
|
+
* Options for Planes.register() and AgentApp.listen().
|
|
366
912
|
*/
|
|
367
913
|
interface RegisterOptions {
|
|
368
914
|
/** Port to listen on (default 3000). */
|
|
@@ -375,16 +921,16 @@ interface RegisterOptions {
|
|
|
375
921
|
onError?: (error: Error) => void;
|
|
376
922
|
}
|
|
377
923
|
/**
|
|
378
|
-
*
|
|
924
|
+
* Planes agent registration namespace.
|
|
379
925
|
* Provides a convenient way to register action handlers as an HTTP server.
|
|
380
926
|
*/
|
|
381
|
-
declare const
|
|
927
|
+
declare const Planes: {
|
|
382
928
|
/**
|
|
383
929
|
* Register action handlers and start an HTTP server to receive action requests.
|
|
384
930
|
*
|
|
385
931
|
* @example
|
|
386
932
|
* ```ts
|
|
387
|
-
*
|
|
933
|
+
* Planes.register([
|
|
388
934
|
* {
|
|
389
935
|
* type: 'send_email',
|
|
390
936
|
* async execute(ctx) {
|
|
@@ -397,48 +943,49 @@ declare const Blueprint: {
|
|
|
397
943
|
*/
|
|
398
944
|
register(handlers: ActionHandler[], options?: RegisterOptions): http.Server;
|
|
399
945
|
};
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Typed webhook event payload from Planes.
|
|
403
|
-
*/
|
|
404
|
-
interface WebhookEvent<T = unknown> {
|
|
405
|
-
/** Event ID (e.g. evt_abc123). */
|
|
406
|
-
id: string;
|
|
407
|
-
/** Event type (e.g. opportunity.created). */
|
|
408
|
-
type: string;
|
|
409
|
-
/** Tenant ID that owns this event. */
|
|
410
|
-
tenant_id: string;
|
|
411
|
-
/** ISO 8601 timestamp of when the event occurred. */
|
|
412
|
-
timestamp: string;
|
|
413
|
-
/** Entity snapshot at the time of the event. */
|
|
414
|
-
data: T;
|
|
415
|
-
/** Metadata about the event trigger. */
|
|
416
|
-
metadata: {
|
|
417
|
-
correlation_id: string;
|
|
418
|
-
triggered_by: 'workflow' | 'api' | 'user';
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
946
|
/**
|
|
422
|
-
*
|
|
947
|
+
* Builder-style agent app. Register action handlers incrementally by type,
|
|
948
|
+
* then start the server with {@link AgentApp.listen}.
|
|
423
949
|
*
|
|
424
|
-
*
|
|
950
|
+
* This is ergonomic sugar over {@link Planes.register} — both share the same
|
|
951
|
+
* underlying server/dispatch implementation ({@link createActionServer}).
|
|
425
952
|
*
|
|
426
|
-
* @
|
|
427
|
-
*
|
|
428
|
-
*
|
|
429
|
-
*
|
|
953
|
+
* @example
|
|
954
|
+
* ```ts
|
|
955
|
+
* createAgentApp()
|
|
956
|
+
* .registerAction('enrich-opportunity', async (ctx) => {
|
|
957
|
+
* // ... enrichment logic
|
|
958
|
+
* return { status: 'completed', output: { enriched: true } };
|
|
959
|
+
* })
|
|
960
|
+
* .listen({ port: 3000 });
|
|
961
|
+
* ```
|
|
430
962
|
*/
|
|
431
|
-
declare
|
|
963
|
+
declare class AgentApp {
|
|
964
|
+
private readonly handlers;
|
|
965
|
+
/**
|
|
966
|
+
* Register a handler for an action type. Accepts a bare `execute` function
|
|
967
|
+
* (the caller does not build the `{ type, execute }` object).
|
|
968
|
+
*
|
|
969
|
+
* @param type - The action type this handler responds to.
|
|
970
|
+
* @param execute - The handler function invoked with the {@link ActionContext}.
|
|
971
|
+
* @returns `this`, for chaining.
|
|
972
|
+
*/
|
|
973
|
+
registerAction(type: string, execute: ActionHandler['execute']): this;
|
|
974
|
+
/**
|
|
975
|
+
* Start an HTTP server that dispatches incoming action requests to the
|
|
976
|
+
* registered handlers. Delegates to the same server factory as
|
|
977
|
+
* {@link Planes.register}.
|
|
978
|
+
*
|
|
979
|
+
* The handler set is snapshotted at call time, so registering further actions
|
|
980
|
+
* after `listen()` does not mutate the running server (parity with
|
|
981
|
+
* `Planes.register`, which snapshots its array).
|
|
982
|
+
*/
|
|
983
|
+
listen(options?: RegisterOptions): http.Server;
|
|
984
|
+
}
|
|
432
985
|
/**
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
* @param body - The raw request body as a string
|
|
436
|
-
* @param signature - The X-Blueprint-Signature header value
|
|
437
|
-
* @param secret - The webhook signing secret
|
|
438
|
-
* @returns The parsed and typed webhook event
|
|
439
|
-
* @throws WebhookVerificationError if signature is invalid
|
|
986
|
+
* Create a new {@link AgentApp} builder.
|
|
440
987
|
*/
|
|
441
|
-
declare function
|
|
988
|
+
declare function createAgentApp(): AgentApp;
|
|
442
989
|
|
|
443
990
|
/**
|
|
444
991
|
* Options for building an OAuth authorization URL.
|
|
@@ -593,4 +1140,4 @@ interface CustomersGeneratedResource {
|
|
|
593
1140
|
createCustomer(data: CreateCustomerDto): Promise<CustomerDto>;
|
|
594
1141
|
}
|
|
595
1142
|
|
|
596
|
-
export { type ActionContext, type ActionHandler, type ActionRequest, type ActionResult, type AgentHandler, AuthenticationError, AuthorizationError, type AuthorizeOptions, BaseResource,
|
|
1143
|
+
export { type ActionContext, type ActionHandler, type ActionRequest, type ActionResult, type ActivityDto, ActivityResource, AgentApp, type AgentHandler, AuthenticationError, AuthorizationError, type AuthorizeOptions, BaseResource, CalendarEventResource, type ContactChannelDto, ContactChannelResource, type ContactDto, type ContactMergeResult, ContactResource, ContractResource, type CreateActivityDto, type CreateContactChannelDto, type CreateContactDto, type CreateCustomerDto, type CreateOpportunityDto, type CreateTagDto, type CustomerDto, CustomerResource, type CustomersGeneratedResource, DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT, DEFAULT_TOKEN_REFRESH_SKEW_MS, type EntityEvent, type EntityEventType, type EntityTagDto, type EntityType, EstimateResource, type ExchangeCodeOptions, type FieldError, type HandlerMap, InventoryResource, InvoiceResource, JobResource, type ListParams, MemoryTokenStorage, NotFoundError, OAuthError, type OpportunitiesGeneratedResource, type OpportunityDto, OpportunityResource, type PaginatedCustomerResponse, type PaginatedOpportunityResponse, type PaginatedResponse, type PaginationMeta, type PermissionLevel, Planes, PlanesClient, type PlanesConfig, PlanesError, PropertyResource, RateLimitError, type RefreshOptions, type RegisterOptions, ServerError, type SetEntityTagsDto, type StoredTokens, type TagDto, type TagEntityDto, TagResource, type TokenResponse, type TokenStorage, type TriggerType, type UpdateContactChannelDto, type UpdateContactDto, type UpdateOpportunityDto, type UpdateTagDto, ValidationError, type WebhookDelivery, type WebhookHandler, type WebhookProcessInput, WebhookRouter, WebhookVerificationError, buildAuthorizeUrl, createAgentApp, exchangeCode, parseWebhookEvent, refreshAccessToken, verifyWebhookSignature };
|