@relayfile/sdk 0.7.8 → 0.7.10
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/setup.d.ts +85 -0
- package/dist/setup.js +159 -0
- package/package.json +2 -2
package/dist/setup.d.ts
CHANGED
|
@@ -74,6 +74,91 @@ export declare class WorkspaceHandle {
|
|
|
74
74
|
waitForNotion(options?: WaitForConnectionOptions): Promise<void>;
|
|
75
75
|
isConnected(provider: WorkspaceIntegrationProvider, connectionId: string): Promise<boolean>;
|
|
76
76
|
disconnectIntegration(provider: WorkspaceIntegrationProvider, _connectionId?: string): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Bind an existing Nango connection to this workspace + provider slot
|
|
79
|
+
* without going through the OAuth re-mint flow. Use this when an operator
|
|
80
|
+
* has already minted the connection out-of-band (Nango UI, third-party
|
|
81
|
+
* setup) and just wants Cloud to start routing sync webhooks for it.
|
|
82
|
+
*
|
|
83
|
+
* The Cloud-side adopt route validates that the Nango connection exists
|
|
84
|
+
* upstream and that its end-user/workspace tag matches this workspace.
|
|
85
|
+
* On success returns the bound `connectionId` and, when a stale prior
|
|
86
|
+
* row was atomically replaced, a `replacedConnectionId` so callers can
|
|
87
|
+
* surface that a migration happened.
|
|
88
|
+
*
|
|
89
|
+
* Failure modes (HTTP body carries `code`):
|
|
90
|
+
* - `connection_not_found` (404): Nango doesn't know this connectionId
|
|
91
|
+
* - `workspace_mismatch` (409): connection belongs to a different
|
|
92
|
+
* workspace; the body includes `pathWorkspaceId` and
|
|
93
|
+
* `connectionWorkspaceId`
|
|
94
|
+
* - `existing_connection_live_or_unknown` (409): a different
|
|
95
|
+
* connection is already bound here and is either still live
|
|
96
|
+
* upstream or has indeterminate state; operator must disconnect
|
|
97
|
+
* first
|
|
98
|
+
*/
|
|
99
|
+
adoptIntegration(provider: WorkspaceIntegrationProvider, connectionId: string, options?: {
|
|
100
|
+
providerConfigKey?: string;
|
|
101
|
+
}): Promise<{
|
|
102
|
+
connectionId: string;
|
|
103
|
+
replacedConnectionId?: string;
|
|
104
|
+
}>;
|
|
105
|
+
/**
|
|
106
|
+
* List the upstream resources the current connection's OAuth grant
|
|
107
|
+
* covers. Today only Atlassian-family providers (`jira`, `confluence`)
|
|
108
|
+
* have meaningful entries here — every Atlassian OAuth grant can cover
|
|
109
|
+
* multiple sites (cloudIds), and the operator needs to bind one of them
|
|
110
|
+
* to this workspace via {@link setIntegrationMetadata} before sync can
|
|
111
|
+
* run.
|
|
112
|
+
*
|
|
113
|
+
* Cloud returns 400 `provider_has_no_accessible_resources` for providers
|
|
114
|
+
* that don't model this concept (currently everything non-Atlassian);
|
|
115
|
+
* that error surfaces here as a `CloudApiError` so callers can handle
|
|
116
|
+
* it explicitly rather than treating an empty list as ambiguous.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```ts
|
|
120
|
+
* const sites = await workspace.listAccessibleResources("jira")
|
|
121
|
+
* if (sites.length > 1) {
|
|
122
|
+
* const choice = await promptOperator(sites)
|
|
123
|
+
* await workspace.setIntegrationMetadata("jira", {
|
|
124
|
+
* cloudId: choice.id,
|
|
125
|
+
* baseUrl: choice.url
|
|
126
|
+
* })
|
|
127
|
+
* }
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
listAccessibleResources(provider: WorkspaceIntegrationProvider | string): Promise<Array<{
|
|
131
|
+
id: string;
|
|
132
|
+
url: string;
|
|
133
|
+
name?: string;
|
|
134
|
+
scopes?: string[];
|
|
135
|
+
avatarUrl?: string;
|
|
136
|
+
}>>;
|
|
137
|
+
/**
|
|
138
|
+
* Set the operator-controlled metadata namespace on the workspace's
|
|
139
|
+
* connection for `provider`. The cloud route forwards the payload to
|
|
140
|
+
* Nango's `setMetadata` (full-replacement, not merge), so the value
|
|
141
|
+
* passed here becomes the connection's metadata wholesale. Callers that
|
|
142
|
+
* want merge semantics should read existing metadata first.
|
|
143
|
+
*
|
|
144
|
+
* This is the second half of the post-OAuth picker flow for Jira /
|
|
145
|
+
* Confluence (see {@link listAccessibleResources}), but is intentionally
|
|
146
|
+
* general-purpose — any provider whose sync requires operator-supplied
|
|
147
|
+
* connection-level config (e.g. a non-default API host) can use this.
|
|
148
|
+
*
|
|
149
|
+
* The cloud side rejects top-level keys that look like Nango plumbing
|
|
150
|
+
* (`_*`, `connection_*`, `auth_*`, `provider_config_key`, `connection_id`)
|
|
151
|
+
* with `code: "invalid_metadata"` so a typo can't clobber the connection.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* await workspace.setIntegrationMetadata("jira", {
|
|
156
|
+
* cloudId: "abc-123",
|
|
157
|
+
* baseUrl: "https://foo.atlassian.net"
|
|
158
|
+
* })
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
setIntegrationMetadata(provider: WorkspaceIntegrationProvider | string, metadata: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
77
162
|
getToken(): string;
|
|
78
163
|
requestJson(options: Omit<CloudRequestOptions, "tokenProvider">): Promise<unknown>;
|
|
79
164
|
mountEnv(options?: WorkspaceMountEnvOptions): WorkspaceMountEnv;
|
package/dist/setup.js
CHANGED
|
@@ -388,6 +388,147 @@ export class WorkspaceHandle {
|
|
|
388
388
|
});
|
|
389
389
|
this._pendingConnections.delete(provider);
|
|
390
390
|
}
|
|
391
|
+
/**
|
|
392
|
+
* Bind an existing Nango connection to this workspace + provider slot
|
|
393
|
+
* without going through the OAuth re-mint flow. Use this when an operator
|
|
394
|
+
* has already minted the connection out-of-band (Nango UI, third-party
|
|
395
|
+
* setup) and just wants Cloud to start routing sync webhooks for it.
|
|
396
|
+
*
|
|
397
|
+
* The Cloud-side adopt route validates that the Nango connection exists
|
|
398
|
+
* upstream and that its end-user/workspace tag matches this workspace.
|
|
399
|
+
* On success returns the bound `connectionId` and, when a stale prior
|
|
400
|
+
* row was atomically replaced, a `replacedConnectionId` so callers can
|
|
401
|
+
* surface that a migration happened.
|
|
402
|
+
*
|
|
403
|
+
* Failure modes (HTTP body carries `code`):
|
|
404
|
+
* - `connection_not_found` (404): Nango doesn't know this connectionId
|
|
405
|
+
* - `workspace_mismatch` (409): connection belongs to a different
|
|
406
|
+
* workspace; the body includes `pathWorkspaceId` and
|
|
407
|
+
* `connectionWorkspaceId`
|
|
408
|
+
* - `existing_connection_live_or_unknown` (409): a different
|
|
409
|
+
* connection is already bound here and is either still live
|
|
410
|
+
* upstream or has indeterminate state; operator must disconnect
|
|
411
|
+
* first
|
|
412
|
+
*/
|
|
413
|
+
async adoptIntegration(provider, connectionId, options = {}) {
|
|
414
|
+
assertProvider(provider);
|
|
415
|
+
const trimmedConnectionId = connectionId?.trim();
|
|
416
|
+
if (!trimmedConnectionId) {
|
|
417
|
+
throw new Error("connectionId is required to adopt an integration");
|
|
418
|
+
}
|
|
419
|
+
const body = { connectionId: trimmedConnectionId };
|
|
420
|
+
const providerConfigKey = options.providerConfigKey?.trim();
|
|
421
|
+
if (providerConfigKey) {
|
|
422
|
+
body.providerConfigKey = providerConfigKey;
|
|
423
|
+
}
|
|
424
|
+
const response = (await this._setup.requestJson({
|
|
425
|
+
operation: "adoptIntegration",
|
|
426
|
+
method: "POST",
|
|
427
|
+
path: `api/v1/workspaces/${encodeURIComponent(this.workspaceId)}/integrations/${encodeURIComponent(provider)}/adopt`,
|
|
428
|
+
body,
|
|
429
|
+
tokenProvider: async () => this.getOrRefreshToken()
|
|
430
|
+
}));
|
|
431
|
+
const boundConnectionId = typeof response.connectionId === "string" && response.connectionId.trim()
|
|
432
|
+
? response.connectionId.trim()
|
|
433
|
+
: trimmedConnectionId;
|
|
434
|
+
const replacedConnectionId = typeof response.replacedConnectionId === "string" &&
|
|
435
|
+
response.replacedConnectionId.trim()
|
|
436
|
+
? response.replacedConnectionId.trim()
|
|
437
|
+
: undefined;
|
|
438
|
+
this._pendingConnections.delete(provider);
|
|
439
|
+
return replacedConnectionId
|
|
440
|
+
? { connectionId: boundConnectionId, replacedConnectionId }
|
|
441
|
+
: { connectionId: boundConnectionId };
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* List the upstream resources the current connection's OAuth grant
|
|
445
|
+
* covers. Today only Atlassian-family providers (`jira`, `confluence`)
|
|
446
|
+
* have meaningful entries here — every Atlassian OAuth grant can cover
|
|
447
|
+
* multiple sites (cloudIds), and the operator needs to bind one of them
|
|
448
|
+
* to this workspace via {@link setIntegrationMetadata} before sync can
|
|
449
|
+
* run.
|
|
450
|
+
*
|
|
451
|
+
* Cloud returns 400 `provider_has_no_accessible_resources` for providers
|
|
452
|
+
* that don't model this concept (currently everything non-Atlassian);
|
|
453
|
+
* that error surfaces here as a `CloudApiError` so callers can handle
|
|
454
|
+
* it explicitly rather than treating an empty list as ambiguous.
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* ```ts
|
|
458
|
+
* const sites = await workspace.listAccessibleResources("jira")
|
|
459
|
+
* if (sites.length > 1) {
|
|
460
|
+
* const choice = await promptOperator(sites)
|
|
461
|
+
* await workspace.setIntegrationMetadata("jira", {
|
|
462
|
+
* cloudId: choice.id,
|
|
463
|
+
* baseUrl: choice.url
|
|
464
|
+
* })
|
|
465
|
+
* }
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
468
|
+
async listAccessibleResources(provider) {
|
|
469
|
+
const normalized = normalizeProviderId(provider);
|
|
470
|
+
const response = (await this._setup.requestJson({
|
|
471
|
+
operation: "listAccessibleResources",
|
|
472
|
+
method: "GET",
|
|
473
|
+
path: `api/v1/workspaces/${encodeURIComponent(this.workspaceId)}/integrations/${encodeURIComponent(normalized)}/accessible-resources`,
|
|
474
|
+
tokenProvider: async () => this.getOrRefreshToken()
|
|
475
|
+
}));
|
|
476
|
+
if (!response || !Array.isArray(response.resources)) {
|
|
477
|
+
return [];
|
|
478
|
+
}
|
|
479
|
+
return response.resources
|
|
480
|
+
.filter((entry) => {
|
|
481
|
+
if (!entry || typeof entry !== "object")
|
|
482
|
+
return false;
|
|
483
|
+
const record = entry;
|
|
484
|
+
return (typeof record.id === "string" &&
|
|
485
|
+
record.id.length > 0 &&
|
|
486
|
+
typeof record.url === "string" &&
|
|
487
|
+
record.url.length > 0);
|
|
488
|
+
})
|
|
489
|
+
.map((entry) => ({ ...entry }));
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Set the operator-controlled metadata namespace on the workspace's
|
|
493
|
+
* connection for `provider`. The cloud route forwards the payload to
|
|
494
|
+
* Nango's `setMetadata` (full-replacement, not merge), so the value
|
|
495
|
+
* passed here becomes the connection's metadata wholesale. Callers that
|
|
496
|
+
* want merge semantics should read existing metadata first.
|
|
497
|
+
*
|
|
498
|
+
* This is the second half of the post-OAuth picker flow for Jira /
|
|
499
|
+
* Confluence (see {@link listAccessibleResources}), but is intentionally
|
|
500
|
+
* general-purpose — any provider whose sync requires operator-supplied
|
|
501
|
+
* connection-level config (e.g. a non-default API host) can use this.
|
|
502
|
+
*
|
|
503
|
+
* The cloud side rejects top-level keys that look like Nango plumbing
|
|
504
|
+
* (`_*`, `connection_*`, `auth_*`, `provider_config_key`, `connection_id`)
|
|
505
|
+
* with `code: "invalid_metadata"` so a typo can't clobber the connection.
|
|
506
|
+
*
|
|
507
|
+
* @example
|
|
508
|
+
* ```ts
|
|
509
|
+
* await workspace.setIntegrationMetadata("jira", {
|
|
510
|
+
* cloudId: "abc-123",
|
|
511
|
+
* baseUrl: "https://foo.atlassian.net"
|
|
512
|
+
* })
|
|
513
|
+
* ```
|
|
514
|
+
*/
|
|
515
|
+
async setIntegrationMetadata(provider, metadata) {
|
|
516
|
+
const normalized = normalizeProviderId(provider);
|
|
517
|
+
if (!isPlainRecord(metadata)) {
|
|
518
|
+
throw new Error("metadata must be a plain object");
|
|
519
|
+
}
|
|
520
|
+
const response = (await this._setup.requestJson({
|
|
521
|
+
operation: "setIntegrationMetadata",
|
|
522
|
+
method: "PUT",
|
|
523
|
+
path: `api/v1/workspaces/${encodeURIComponent(this.workspaceId)}/integrations/${encodeURIComponent(normalized)}/metadata`,
|
|
524
|
+
body: { metadata },
|
|
525
|
+
tokenProvider: async () => this.getOrRefreshToken()
|
|
526
|
+
}));
|
|
527
|
+
if (isPlainRecord(response?.metadata)) {
|
|
528
|
+
return { ...response.metadata };
|
|
529
|
+
}
|
|
530
|
+
throw new Error("invalid cloud response: expected metadata to be a plain object");
|
|
531
|
+
}
|
|
391
532
|
getToken() {
|
|
392
533
|
return this._token;
|
|
393
534
|
}
|
|
@@ -592,6 +733,24 @@ class MountedWorkspaceHandleImpl {
|
|
|
592
733
|
await safeStopLauncher(this.launcherInstance);
|
|
593
734
|
}
|
|
594
735
|
}
|
|
736
|
+
// The metadata + accessible-resources verbs accept `WorkspaceIntegrationProvider | string`
|
|
737
|
+
// so operators can target dynamically-discovered providers (Composio toolkit
|
|
738
|
+
// slugs, future Atlassian-family additions) without an SDK release. We still
|
|
739
|
+
// strip trailing whitespace and lower-case so consumers don't have to.
|
|
740
|
+
function normalizeProviderId(provider) {
|
|
741
|
+
const trimmed = typeof provider === "string" ? provider.trim() : "";
|
|
742
|
+
if (!trimmed) {
|
|
743
|
+
throw new Error("provider is required");
|
|
744
|
+
}
|
|
745
|
+
return trimmed.toLowerCase();
|
|
746
|
+
}
|
|
747
|
+
function isPlainRecord(value) {
|
|
748
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
749
|
+
return false;
|
|
750
|
+
}
|
|
751
|
+
const proto = Object.getPrototypeOf(value);
|
|
752
|
+
return proto === Object.prototype || proto === null;
|
|
753
|
+
}
|
|
595
754
|
function assertProvider(provider) {
|
|
596
755
|
if (!WORKSPACE_INTEGRATION_PROVIDERS.includes(provider)) {
|
|
597
756
|
throw new UnknownProviderError(provider);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relayfile/sdk",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.10",
|
|
4
4
|
"description": "TypeScript SDK for relayfile — real-time filesystem for humans and agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"prepublishOnly": "npm run build"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@relayfile/core": "0.7.
|
|
25
|
+
"@relayfile/core": "0.7.10"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"typescript": "^5.7.3",
|