replicas-engine 0.1.595 → 0.1.597
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/src/index.js +326 -640
- package/package.json +3 -2
- package/workspace-sdk/index.d.ts +14 -0
- package/workspace-sdk/index.js +118 -0
- package/workspace-sdk/index.test.ts +94 -0
- package/workspace-sdk/package.json +14 -0
- package/workspace-sdk/shared/routes/integrations.d.ts +11 -0
- package/workspace-sdk/shared/routes/plugins.d.ts +119 -0
- package/workspace-sdk/shared/workspace-sdk.d.ts +58 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "replicas-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.597",
|
|
4
4
|
"description": "Lightweight API server for Replicas workspaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -10,13 +10,14 @@
|
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
13
|
+
"workspace-sdk",
|
|
13
14
|
"scripts/opencode",
|
|
14
15
|
"scripts/engine-watchdog.sh",
|
|
15
16
|
"scripts/lockmem.c"
|
|
16
17
|
],
|
|
17
18
|
"scripts": {
|
|
18
19
|
"dev": "tsx watch --conditions=development src/index.ts",
|
|
19
|
-
"build": "tsup",
|
|
20
|
+
"build": "bun run --cwd ../shared build && node scripts/generate-workspace-sdk-types.mjs && tsup",
|
|
20
21
|
"dev-pack": "bun run build && node scripts/pack-dev-tarball.mjs",
|
|
21
22
|
"start": "node dist/src/index.js",
|
|
22
23
|
"dev-sync": "scripts/dev-sync.sh",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ReplicasSdk } from './shared/workspace-sdk';
|
|
2
|
+
|
|
3
|
+
export type {
|
|
4
|
+
PluginConnectionStatus,
|
|
5
|
+
PluginId,
|
|
6
|
+
PluginScope,
|
|
7
|
+
PluginToolSchema,
|
|
8
|
+
ProviderRequestOptions,
|
|
9
|
+
ReplicasSdk,
|
|
10
|
+
WorkspaceIntegrationsResponse,
|
|
11
|
+
WorkspacePluginSummary,
|
|
12
|
+
} from './shared/workspace-sdk';
|
|
13
|
+
export declare const replicas: ReplicasSdk;
|
|
14
|
+
export default replicas;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
function config() {
|
|
2
|
+
const monolithUrl = process.env.REPLICAS_MONOLITH_URL;
|
|
3
|
+
const engineSecret = process.env.REPLICAS_ENGINE_SECRET;
|
|
4
|
+
const workspaceId = process.env.REPLICAS_WORKSPACE_ID;
|
|
5
|
+
if (!monolithUrl || !engineSecret || !workspaceId) {
|
|
6
|
+
throw new Error('This process is missing Replicas workspace credentials');
|
|
7
|
+
}
|
|
8
|
+
return { monolithUrl: monolithUrl.replace(/\/$/, ''), engineSecret, workspaceId };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function request(path, options = {}) {
|
|
12
|
+
const { monolithUrl, engineSecret, workspaceId } = config();
|
|
13
|
+
const headers = new Headers(options.headers);
|
|
14
|
+
headers.set('Authorization', `Bearer ${engineSecret}`);
|
|
15
|
+
headers.set('X-Workspace-Id', workspaceId);
|
|
16
|
+
if (options.body !== undefined) headers.set('Content-Type', 'application/json');
|
|
17
|
+
const response = await fetch(`${monolithUrl}${path}`, {
|
|
18
|
+
...options,
|
|
19
|
+
headers,
|
|
20
|
+
body: options.body === undefined ? undefined : JSON.stringify(options.body),
|
|
21
|
+
});
|
|
22
|
+
if (response.status === 204) return undefined;
|
|
23
|
+
const text = await response.text();
|
|
24
|
+
let data;
|
|
25
|
+
try {
|
|
26
|
+
data = JSON.parse(text);
|
|
27
|
+
} catch {
|
|
28
|
+
data = { error: text };
|
|
29
|
+
}
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
throw new Error(data && typeof data.error === 'string' ? data.error : `Replicas request failed (${response.status})`);
|
|
32
|
+
}
|
|
33
|
+
return data;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function providerPath(path) {
|
|
37
|
+
if (
|
|
38
|
+
typeof path !== 'string' ||
|
|
39
|
+
!path.startsWith('/') ||
|
|
40
|
+
path.startsWith('//') ||
|
|
41
|
+
path.split('/').includes('..')
|
|
42
|
+
) {
|
|
43
|
+
throw new Error('Provider API paths must be relative and start with /');
|
|
44
|
+
}
|
|
45
|
+
return path;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const integrations = {
|
|
49
|
+
list: () => request('/v1/engine/integrations'),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const plugins = {
|
|
53
|
+
async list() {
|
|
54
|
+
return (await integrations.list()).plugins;
|
|
55
|
+
},
|
|
56
|
+
search: (input) => request('/v1/engine/plugins/search', { method: 'POST', body: input }),
|
|
57
|
+
describe: (input) => request('/v1/engine/plugins/describe', { method: 'POST', body: input }),
|
|
58
|
+
execute: (input) => request('/v1/engine/plugins/execute', { method: 'POST', body: input }),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const linear = {
|
|
62
|
+
graphql: ({ query, variables, operationName }) =>
|
|
63
|
+
request('/v1/engine/linear/graphql', {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
body: { query, variables, operationName },
|
|
66
|
+
}),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const slack = {
|
|
70
|
+
call: (method, arguments_ = {}) =>
|
|
71
|
+
request('/v1/engine/slack/api', {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
body: { method, arguments: arguments_ },
|
|
74
|
+
}),
|
|
75
|
+
attachThread: ({ channel, threadTs }) =>
|
|
76
|
+
request('/v1/slack/threads/attach', {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
body: { channel_id: channel, thread_ts: threadTs },
|
|
79
|
+
}),
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const google = {
|
|
83
|
+
request(path, options = {}) {
|
|
84
|
+
const normalized = path.startsWith('/v1/gdrive/')
|
|
85
|
+
? path
|
|
86
|
+
: `/v1/gdrive/${path.replace(/^\/+/, '')}`;
|
|
87
|
+
if (!normalized.startsWith('/v1/gdrive/')) throw new Error('Google paths must use the Replicas Google gateway');
|
|
88
|
+
return request(normalized, options);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const github = {
|
|
93
|
+
request: (path, options = {}) =>
|
|
94
|
+
request('/v1/engine/github/api', {
|
|
95
|
+
method: 'POST',
|
|
96
|
+
body: { path: providerPath(path), method: options.method, body: options.body },
|
|
97
|
+
}),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const gitlab = {
|
|
101
|
+
request: (path, options = {}) =>
|
|
102
|
+
request('/v1/engine/gitlab/api', {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
body: {
|
|
105
|
+
path: providerPath(path),
|
|
106
|
+
method: options.method,
|
|
107
|
+
body: options.body,
|
|
108
|
+
host: options.host,
|
|
109
|
+
},
|
|
110
|
+
}),
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const sentry = {
|
|
114
|
+
request: (path) => request('/v1/engine/sentry/api', { method: 'POST', body: { path } }),
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const replicas = { integrations, plugins, linear, slack, google, github, gitlab, sentry };
|
|
118
|
+
export default replicas;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from 'bun:test';
|
|
2
|
+
import { replicas } from './index.js';
|
|
3
|
+
|
|
4
|
+
const previous = {
|
|
5
|
+
url: process.env.REPLICAS_MONOLITH_URL,
|
|
6
|
+
secret: process.env.REPLICAS_ENGINE_SECRET,
|
|
7
|
+
workspace: process.env.REPLICAS_WORKSPACE_ID,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
if (previous.url === undefined) delete process.env.REPLICAS_MONOLITH_URL;
|
|
12
|
+
else process.env.REPLICAS_MONOLITH_URL = previous.url;
|
|
13
|
+
if (previous.secret === undefined) delete process.env.REPLICAS_ENGINE_SECRET;
|
|
14
|
+
else process.env.REPLICAS_ENGINE_SECRET = previous.secret;
|
|
15
|
+
if (previous.workspace === undefined) delete process.env.REPLICAS_WORKSPACE_ID;
|
|
16
|
+
else process.env.REPLICAS_WORKSPACE_ID = previous.workspace;
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('@replicas/sdk', () => {
|
|
20
|
+
test('sends only workspace identity to the scoped gateway', async () => {
|
|
21
|
+
let observed: { path: string; authorization: string | null; workspace: string | null; body: unknown } | null = null;
|
|
22
|
+
const server = Bun.serve({
|
|
23
|
+
port: 0,
|
|
24
|
+
async fetch(request) {
|
|
25
|
+
observed = {
|
|
26
|
+
path: new URL(request.url).pathname,
|
|
27
|
+
authorization: request.headers.get('authorization'),
|
|
28
|
+
workspace: request.headers.get('x-workspace-id'),
|
|
29
|
+
body: await request.json(),
|
|
30
|
+
};
|
|
31
|
+
return Response.json({ data: { ok: true }, logId: 'log-1' });
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
process.env.REPLICAS_MONOLITH_URL = server.url.toString().replace(/\/$/, '');
|
|
35
|
+
process.env.REPLICAS_ENGINE_SECRET = 'engine-secret';
|
|
36
|
+
process.env.REPLICAS_WORKSPACE_ID = 'workspace-1';
|
|
37
|
+
try {
|
|
38
|
+
await replicas.plugins.execute({
|
|
39
|
+
plugin: 'stripe',
|
|
40
|
+
tool: 'STRIPE_RETRIEVE_BALANCE',
|
|
41
|
+
});
|
|
42
|
+
expect(observed).toEqual({
|
|
43
|
+
path: '/v1/engine/plugins/execute',
|
|
44
|
+
authorization: 'Bearer engine-secret',
|
|
45
|
+
workspace: 'workspace-1',
|
|
46
|
+
body: { plugin: 'stripe', tool: 'STRIPE_RETRIEVE_BALANCE' },
|
|
47
|
+
});
|
|
48
|
+
} finally {
|
|
49
|
+
server.stop(true);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('refuses to run outside an authenticated workspace', async () => {
|
|
54
|
+
delete process.env.REPLICAS_MONOLITH_URL;
|
|
55
|
+
delete process.env.REPLICAS_ENGINE_SECRET;
|
|
56
|
+
delete process.env.REPLICAS_WORKSPACE_ID;
|
|
57
|
+
await expect(replicas.plugins.list()).rejects.toThrow('missing Replicas workspace credentials');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('keeps native provider credentials behind the workspace gateway', async () => {
|
|
61
|
+
let observed: { path: string; authorization: string | null; body: unknown } | null = null;
|
|
62
|
+
const server = Bun.serve({
|
|
63
|
+
port: 0,
|
|
64
|
+
async fetch(request) {
|
|
65
|
+
observed = {
|
|
66
|
+
path: new URL(request.url).pathname,
|
|
67
|
+
authorization: request.headers.get('authorization'),
|
|
68
|
+
body: await request.json(),
|
|
69
|
+
};
|
|
70
|
+
return Response.json({ login: 'replicas' });
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
process.env.REPLICAS_MONOLITH_URL = server.url.toString().replace(/\/$/, '');
|
|
74
|
+
process.env.REPLICAS_ENGINE_SECRET = 'engine-secret';
|
|
75
|
+
process.env.REPLICAS_WORKSPACE_ID = 'workspace-1';
|
|
76
|
+
try {
|
|
77
|
+
await replicas.github.request('/user');
|
|
78
|
+
expect(observed).toEqual({
|
|
79
|
+
path: '/v1/engine/github/api',
|
|
80
|
+
authorization: 'Bearer engine-secret',
|
|
81
|
+
body: { path: '/user' },
|
|
82
|
+
});
|
|
83
|
+
} finally {
|
|
84
|
+
server.stop(true);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('rejects provider URLs that can escape the allowed origin', async () => {
|
|
89
|
+
process.env.REPLICAS_MONOLITH_URL = 'https://api.example.com';
|
|
90
|
+
process.env.REPLICAS_ENGINE_SECRET = 'engine-secret';
|
|
91
|
+
process.env.REPLICAS_WORKSPACE_ID = 'workspace-1';
|
|
92
|
+
expect(() => replicas.github.request('//attacker.example/path')).toThrow('relative');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Generated from shared/src by generate-workspace-sdk-types.mjs; do not edit.
|
|
2
|
+
export interface NativeIntegrationStatus {
|
|
3
|
+
github: boolean;
|
|
4
|
+
gitlab: boolean;
|
|
5
|
+
linear: boolean;
|
|
6
|
+
slack: boolean;
|
|
7
|
+
google: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface WorkspaceNativeIntegrationStatus extends NativeIntegrationStatus {
|
|
10
|
+
sentry: boolean;
|
|
11
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Generated from shared/src by generate-workspace-sdk-types.mjs; do not edit.
|
|
2
|
+
import type { WorkspaceNativeIntegrationStatus } from './integrations';
|
|
3
|
+
export declare const PLUGIN_CATALOG: readonly [{
|
|
4
|
+
readonly id: "attio";
|
|
5
|
+
readonly toolkit: "attio";
|
|
6
|
+
readonly name: "Attio";
|
|
7
|
+
readonly description: "Search CRM records, people, companies, lists, and notes.";
|
|
8
|
+
}, {
|
|
9
|
+
readonly id: "clickhouse";
|
|
10
|
+
readonly toolkit: "clickhouse";
|
|
11
|
+
readonly name: "ClickHouse";
|
|
12
|
+
readonly description: "Inspect databases, tables, schemas, and query results.";
|
|
13
|
+
}, {
|
|
14
|
+
readonly id: "googleads";
|
|
15
|
+
readonly toolkit: "googleads";
|
|
16
|
+
readonly name: "Google Ads";
|
|
17
|
+
readonly description: "Inspect campaigns, customers, accounts, and reporting data.";
|
|
18
|
+
}, {
|
|
19
|
+
readonly id: "googledocs";
|
|
20
|
+
readonly toolkit: "googledocs";
|
|
21
|
+
readonly name: "Google Docs";
|
|
22
|
+
readonly description: "Read documents, structure, comments, and exported content.";
|
|
23
|
+
}, {
|
|
24
|
+
readonly id: "googledrive";
|
|
25
|
+
readonly toolkit: "googledrive";
|
|
26
|
+
readonly name: "Google Drive";
|
|
27
|
+
readonly description: "Search files, folders, metadata, permissions, and shared drives.";
|
|
28
|
+
}, {
|
|
29
|
+
readonly id: "googlesearch";
|
|
30
|
+
readonly toolkit: "google_search_console";
|
|
31
|
+
readonly name: "Google Search";
|
|
32
|
+
readonly description: "Inspect Search Console performance, indexing, sites, and sitemaps.";
|
|
33
|
+
}, {
|
|
34
|
+
readonly id: "googlesheets";
|
|
35
|
+
readonly toolkit: "googlesheets";
|
|
36
|
+
readonly name: "Google Sheets";
|
|
37
|
+
readonly description: "Read spreadsheets, worksheets, ranges, tables, and charts.";
|
|
38
|
+
}, {
|
|
39
|
+
readonly id: "posthog";
|
|
40
|
+
readonly toolkit: "posthog";
|
|
41
|
+
readonly name: "PostHog";
|
|
42
|
+
readonly description: "Inspect projects, events, insights, dashboards, flags, and recordings.";
|
|
43
|
+
}, {
|
|
44
|
+
readonly id: "stripe";
|
|
45
|
+
readonly toolkit: "stripe";
|
|
46
|
+
readonly name: "Stripe";
|
|
47
|
+
readonly description: "Inspect customers, payments, invoices, subscriptions, and balances.";
|
|
48
|
+
}];
|
|
49
|
+
export type PluginId = (typeof PLUGIN_CATALOG)[number]['id'];
|
|
50
|
+
export type PluginScope = 'organization' | 'personal';
|
|
51
|
+
export declare const PLUGIN_CONNECTION_STATUSES: readonly ["initiated", "active", "expired", "failed"];
|
|
52
|
+
export type PluginConnectionStatus = (typeof PLUGIN_CONNECTION_STATUSES)[number];
|
|
53
|
+
export interface PluginCatalogItem {
|
|
54
|
+
id: PluginId;
|
|
55
|
+
toolkit: string;
|
|
56
|
+
name: string;
|
|
57
|
+
description: string;
|
|
58
|
+
}
|
|
59
|
+
export interface PluginConnectionSummary {
|
|
60
|
+
id: string;
|
|
61
|
+
pluginId: PluginId;
|
|
62
|
+
scope: PluginScope;
|
|
63
|
+
status: PluginConnectionStatus;
|
|
64
|
+
accountLabel?: string;
|
|
65
|
+
connectedAt?: string;
|
|
66
|
+
}
|
|
67
|
+
export interface PluginConnectionsResponse {
|
|
68
|
+
plugins: PluginCatalogItem[];
|
|
69
|
+
organization: PluginConnectionSummary[];
|
|
70
|
+
personal: PluginConnectionSummary[];
|
|
71
|
+
}
|
|
72
|
+
export interface PluginStartOAuthResponse {
|
|
73
|
+
authorizationUrl: string;
|
|
74
|
+
}
|
|
75
|
+
export interface PluginConnectionDeleteResponse {
|
|
76
|
+
success: boolean;
|
|
77
|
+
}
|
|
78
|
+
export interface WorkspacePluginSummary extends PluginCatalogItem {
|
|
79
|
+
connected: boolean;
|
|
80
|
+
scope?: PluginScope;
|
|
81
|
+
status?: PluginConnectionStatus;
|
|
82
|
+
accountLabel?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface WorkspaceIntegrationsResponse {
|
|
85
|
+
plugins: WorkspacePluginSummary[];
|
|
86
|
+
native: WorkspaceNativeIntegrationStatus;
|
|
87
|
+
}
|
|
88
|
+
export interface PluginSearchRequest {
|
|
89
|
+
plugin: PluginId;
|
|
90
|
+
query: string;
|
|
91
|
+
}
|
|
92
|
+
export interface PluginToolSchema {
|
|
93
|
+
slug: string;
|
|
94
|
+
toolkit: string;
|
|
95
|
+
description: string;
|
|
96
|
+
inputSchema: Record<string, unknown>;
|
|
97
|
+
outputSchema?: Record<string, unknown>;
|
|
98
|
+
}
|
|
99
|
+
export interface PluginSearchResponse {
|
|
100
|
+
tools: PluginToolSchema[];
|
|
101
|
+
}
|
|
102
|
+
export interface PluginDescribeRequest {
|
|
103
|
+
plugin: PluginId;
|
|
104
|
+
tools: string[];
|
|
105
|
+
}
|
|
106
|
+
export interface PluginDescribeResponse {
|
|
107
|
+
tools: PluginToolSchema[];
|
|
108
|
+
}
|
|
109
|
+
export interface PluginExecuteRequest {
|
|
110
|
+
plugin: PluginId;
|
|
111
|
+
tool: string;
|
|
112
|
+
arguments?: Record<string, unknown>;
|
|
113
|
+
}
|
|
114
|
+
export interface PluginExecuteResponse {
|
|
115
|
+
data: unknown;
|
|
116
|
+
logId: string;
|
|
117
|
+
}
|
|
118
|
+
export declare function isPluginId(value: string): value is PluginId;
|
|
119
|
+
export declare function isPluginConnectionStatus(value: string): value is PluginConnectionStatus;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Generated from shared/src by generate-workspace-sdk-types.mjs; do not edit.
|
|
2
|
+
import type { PluginDescribeRequest, PluginDescribeResponse, PluginExecuteRequest, PluginExecuteResponse, PluginSearchRequest, PluginSearchResponse, WorkspaceIntegrationsResponse, WorkspacePluginSummary } from './routes/plugins';
|
|
3
|
+
export type { PluginConnectionStatus, PluginId, PluginScope, PluginToolSchema, WorkspaceIntegrationsResponse, WorkspacePluginSummary, } from './routes/plugins';
|
|
4
|
+
export interface ProviderRequestOptions {
|
|
5
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
6
|
+
body?: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface ReplicasSdk {
|
|
9
|
+
integrations: {
|
|
10
|
+
list(): Promise<WorkspaceIntegrationsResponse>;
|
|
11
|
+
};
|
|
12
|
+
plugins: {
|
|
13
|
+
list(): Promise<WorkspacePluginSummary[]>;
|
|
14
|
+
search(input: PluginSearchRequest): Promise<PluginSearchResponse>;
|
|
15
|
+
describe(input: PluginDescribeRequest): Promise<PluginDescribeResponse>;
|
|
16
|
+
execute<T = unknown>(input: PluginExecuteRequest): Promise<Omit<PluginExecuteResponse, 'data'> & {
|
|
17
|
+
data: T;
|
|
18
|
+
}>;
|
|
19
|
+
};
|
|
20
|
+
linear: {
|
|
21
|
+
graphql<T = unknown>(input: {
|
|
22
|
+
query: string;
|
|
23
|
+
variables?: Record<string, unknown> | null;
|
|
24
|
+
operationName?: string | null;
|
|
25
|
+
}): Promise<T>;
|
|
26
|
+
};
|
|
27
|
+
slack: {
|
|
28
|
+
call<T = unknown>(method: string, args?: Record<string, unknown>): Promise<T>;
|
|
29
|
+
attachThread(input: {
|
|
30
|
+
channel: string;
|
|
31
|
+
threadTs: string;
|
|
32
|
+
}): Promise<{
|
|
33
|
+
thread: unknown;
|
|
34
|
+
workspace: {
|
|
35
|
+
id: string;
|
|
36
|
+
name: string;
|
|
37
|
+
};
|
|
38
|
+
}>;
|
|
39
|
+
};
|
|
40
|
+
google: {
|
|
41
|
+
request<T = unknown>(path: string, options?: {
|
|
42
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
43
|
+
body?: unknown;
|
|
44
|
+
headers?: HeadersInit;
|
|
45
|
+
}): Promise<T>;
|
|
46
|
+
};
|
|
47
|
+
github: {
|
|
48
|
+
request<T = unknown>(path: string, options?: ProviderRequestOptions): Promise<T>;
|
|
49
|
+
};
|
|
50
|
+
gitlab: {
|
|
51
|
+
request<T = unknown>(path: string, options?: ProviderRequestOptions & {
|
|
52
|
+
host?: string;
|
|
53
|
+
}): Promise<T>;
|
|
54
|
+
};
|
|
55
|
+
sentry: {
|
|
56
|
+
request<T = unknown>(path: string): Promise<T>;
|
|
57
|
+
};
|
|
58
|
+
}
|