crewly 1.20.265 → 1.20.267

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.
Files changed (125) hide show
  1. package/config/skills/_common/lib.sh +15 -2
  2. package/config/skills/agent/core/app-data/SKILL.md +13 -1
  3. package/config/skills/agent/core/app-data/execute.sh +41 -5
  4. package/config/skills/agent/core/app-data/execute.test.sh +47 -1
  5. package/config/skills/agent/core/publish-app/SKILL.md +1 -1
  6. package/config/skills/agent/core/slack-file/SKILL.md +53 -0
  7. package/config/skills/agent/core/slack-file/execute.sh +72 -0
  8. package/config/skills/agent/core/slack-file/execute.test.sh +34 -0
  9. package/dist/backend/backend/src/constants.d.ts +59 -0
  10. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  11. package/dist/backend/backend/src/constants.js +59 -0
  12. package/dist/backend/backend/src/constants.js.map +1 -1
  13. package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts +7 -0
  14. package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts.map +1 -1
  15. package/dist/backend/backend/src/controllers/apps/apps.controller.js +23 -0
  16. package/dist/backend/backend/src/controllers/apps/apps.controller.js.map +1 -1
  17. package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts +9 -0
  18. package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts.map +1 -1
  19. package/dist/backend/backend/src/controllers/apps/apps.routes.js +19 -1
  20. package/dist/backend/backend/src/controllers/apps/apps.routes.js.map +1 -1
  21. package/dist/backend/backend/src/controllers/connector/connector.routes.d.ts.map +1 -1
  22. package/dist/backend/backend/src/controllers/connector/connector.routes.js +6 -1
  23. package/dist/backend/backend/src/controllers/connector/connector.routes.js.map +1 -1
  24. package/dist/backend/backend/src/controllers/connector/remote-mcp-proxy.controller.d.ts +69 -0
  25. package/dist/backend/backend/src/controllers/connector/remote-mcp-proxy.controller.d.ts.map +1 -0
  26. package/dist/backend/backend/src/controllers/connector/remote-mcp-proxy.controller.js +203 -0
  27. package/dist/backend/backend/src/controllers/connector/remote-mcp-proxy.controller.js.map +1 -0
  28. package/dist/backend/backend/src/controllers/connector/remote-mcp.controller.d.ts +16 -0
  29. package/dist/backend/backend/src/controllers/connector/remote-mcp.controller.d.ts.map +1 -1
  30. package/dist/backend/backend/src/controllers/connector/remote-mcp.controller.js +126 -5
  31. package/dist/backend/backend/src/controllers/connector/remote-mcp.controller.js.map +1 -1
  32. package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
  33. package/dist/backend/backend/src/controllers/slack/slack.controller.js +37 -0
  34. package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
  35. package/dist/backend/backend/src/index.d.ts.map +1 -1
  36. package/dist/backend/backend/src/index.js +5 -0
  37. package/dist/backend/backend/src/index.js.map +1 -1
  38. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  39. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +2 -1
  40. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  41. package/dist/backend/backend/src/services/apps/apps-cloud.client.d.ts +2 -0
  42. package/dist/backend/backend/src/services/apps/apps-cloud.client.d.ts.map +1 -1
  43. package/dist/backend/backend/src/services/apps/apps-cloud.client.js +1 -0
  44. package/dist/backend/backend/src/services/apps/apps-cloud.client.js.map +1 -1
  45. package/dist/backend/backend/src/services/apps/apps.service.d.ts +17 -0
  46. package/dist/backend/backend/src/services/apps/apps.service.d.ts.map +1 -1
  47. package/dist/backend/backend/src/services/apps/apps.service.js +24 -0
  48. package/dist/backend/backend/src/services/apps/apps.service.js.map +1 -1
  49. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.d.ts.map +1 -1
  50. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js +4 -3
  51. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js.map +1 -1
  52. package/dist/backend/backend/src/services/connector/remote-mcp-auth-card.d.ts +43 -0
  53. package/dist/backend/backend/src/services/connector/remote-mcp-auth-card.d.ts.map +1 -0
  54. package/dist/backend/backend/src/services/connector/remote-mcp-auth-card.js +98 -0
  55. package/dist/backend/backend/src/services/connector/remote-mcp-auth-card.js.map +1 -0
  56. package/dist/backend/backend/src/services/connector/remote-mcp-auth.service.d.ts +277 -0
  57. package/dist/backend/backend/src/services/connector/remote-mcp-auth.service.d.ts.map +1 -0
  58. package/dist/backend/backend/src/services/connector/remote-mcp-auth.service.js +719 -0
  59. package/dist/backend/backend/src/services/connector/remote-mcp-auth.service.js.map +1 -0
  60. package/dist/backend/backend/src/services/connector/remote-mcp-launch.service.d.ts +29 -5
  61. package/dist/backend/backend/src/services/connector/remote-mcp-launch.service.d.ts.map +1 -1
  62. package/dist/backend/backend/src/services/connector/remote-mcp-launch.service.js +40 -12
  63. package/dist/backend/backend/src/services/connector/remote-mcp-launch.service.js.map +1 -1
  64. package/dist/backend/backend/src/services/connector/remote-mcp-oauth.d.ts +196 -0
  65. package/dist/backend/backend/src/services/connector/remote-mcp-oauth.d.ts.map +1 -0
  66. package/dist/backend/backend/src/services/connector/remote-mcp-oauth.js +351 -0
  67. package/dist/backend/backend/src/services/connector/remote-mcp-oauth.js.map +1 -0
  68. package/dist/backend/backend/src/services/connector/remote-mcp-probe.service.d.ts +3 -1
  69. package/dist/backend/backend/src/services/connector/remote-mcp-probe.service.d.ts.map +1 -1
  70. package/dist/backend/backend/src/services/connector/remote-mcp-probe.service.js +8 -0
  71. package/dist/backend/backend/src/services/connector/remote-mcp-probe.service.js.map +1 -1
  72. package/dist/backend/backend/src/services/core/credential-files.d.ts.map +1 -1
  73. package/dist/backend/backend/src/services/core/credential-files.js +2 -0
  74. package/dist/backend/backend/src/services/core/credential-files.js.map +1 -1
  75. package/dist/backend/backend/src/services/slack/slack-file-fetch.service.d.ts +119 -0
  76. package/dist/backend/backend/src/services/slack/slack-file-fetch.service.d.ts.map +1 -0
  77. package/dist/backend/backend/src/services/slack/slack-file-fetch.service.js +415 -0
  78. package/dist/backend/backend/src/services/slack/slack-file-fetch.service.js.map +1 -0
  79. package/dist/backend/backend/src/services/slack/slack-file-ref.d.ts +69 -0
  80. package/dist/backend/backend/src/services/slack/slack-file-ref.d.ts.map +1 -0
  81. package/dist/backend/backend/src/services/slack/slack-file-ref.js +177 -0
  82. package/dist/backend/backend/src/services/slack/slack-file-ref.js.map +1 -0
  83. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
  84. package/dist/backend/backend/src/services/slack/slack-initializer.js +16 -0
  85. package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
  86. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +15 -0
  87. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  88. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +84 -1
  89. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  90. package/dist/backend/backend/src/types/slack.types.d.ts +11 -0
  91. package/dist/backend/backend/src/types/slack.types.d.ts.map +1 -1
  92. package/dist/backend/backend/src/types/slack.types.js.map +1 -1
  93. package/dist/backend/build-info.json +2 -2
  94. package/dist/cli/backend/src/constants.d.ts +59 -0
  95. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  96. package/dist/cli/backend/src/constants.js +59 -0
  97. package/dist/cli/backend/src/constants.js.map +1 -1
  98. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  99. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +2 -1
  100. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  101. package/dist/cli/backend/src/services/connector/remote-mcp-launch.service.d.ts +29 -5
  102. package/dist/cli/backend/src/services/connector/remote-mcp-launch.service.d.ts.map +1 -1
  103. package/dist/cli/backend/src/services/connector/remote-mcp-launch.service.js +40 -12
  104. package/dist/cli/backend/src/services/connector/remote-mcp-launch.service.js.map +1 -1
  105. package/dist/cli/backend/src/services/core/credential-files.d.ts.map +1 -1
  106. package/dist/cli/backend/src/services/core/credential-files.js +2 -0
  107. package/dist/cli/backend/src/services/core/credential-files.js.map +1 -1
  108. package/dist/cli/backend/src/services/slack/slack-file-fetch.service.d.ts +119 -0
  109. package/dist/cli/backend/src/services/slack/slack-file-fetch.service.d.ts.map +1 -0
  110. package/dist/cli/backend/src/services/slack/slack-file-fetch.service.js +415 -0
  111. package/dist/cli/backend/src/services/slack/slack-file-fetch.service.js.map +1 -0
  112. package/dist/cli/backend/src/services/slack/slack-file-ref.d.ts +69 -0
  113. package/dist/cli/backend/src/services/slack/slack-file-ref.d.ts.map +1 -0
  114. package/dist/cli/backend/src/services/slack/slack-file-ref.js +177 -0
  115. package/dist/cli/backend/src/services/slack/slack-file-ref.js.map +1 -0
  116. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts +15 -0
  117. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  118. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +84 -1
  119. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  120. package/dist/cli/backend/src/types/slack.types.d.ts +11 -0
  121. package/dist/cli/backend/src/types/slack.types.d.ts.map +1 -1
  122. package/dist/cli/backend/src/types/slack.types.js.map +1 -1
  123. package/frontend/dist/assets/{index-83188f0b.js → index-7e5ebf78.js} +111 -111
  124. package/frontend/dist/index.html +1 -1
  125. package/package.json +1 -1
@@ -0,0 +1,98 @@
1
+ /**
2
+ * The Slack side of remote MCP sign-in: a card in the owner's DM whose
3
+ * button opens a single-use Crewly Cloud ticket link (never the server URL
4
+ * or its key), and a receipt when the sign-in finished.
5
+ *
6
+ * Posted in the owner's DM with the agent whose call needed it (that
7
+ * agent's bot), else the orchestrator's / workspace bot. A DM, so only the
8
+ * owner sees the link.
9
+ *
10
+ * @module services/connector/remote-mcp-auth-card
11
+ */
12
+ import { CLOUD_DISCONNECT_NOTICE_CONSTANTS } from '../../../../config/constants.js';
13
+ const SLACK = CLOUD_DISCONNECT_NOTICE_CONSTANTS;
14
+ /**
15
+ * Build the card. Pure, so the wording is testable.
16
+ *
17
+ * @param label - Server name ("Zoho")
18
+ * @param url - Ticket link
19
+ * @param expiresAt - When the link stops working
20
+ * @param now - Clock (tests)
21
+ * @returns Fallback text and Block Kit blocks
22
+ */
23
+ export function buildRemoteMcpAuthCard(label, url, expiresAt, now = Date.now()) {
24
+ const hours = Math.max(1, Math.round((new Date(expiresAt).getTime() - now) / 3_600_000));
25
+ const safe = label.replace(/[<>&*_~`]/g, '');
26
+ return {
27
+ text: `${safe} needs you to sign in once. Tap to authorize (works on your phone).`,
28
+ blocks: [
29
+ {
30
+ type: 'section',
31
+ text: { type: 'mrkdwn', text: `*${safe}* needs you to sign in once.\nTap to authorize — it works on your phone.` },
32
+ accessory: { type: 'button', text: { type: 'plain_text', text: 'Authorize' }, url, style: 'primary' },
33
+ },
34
+ {
35
+ type: 'context',
36
+ elements: [{ type: 'mrkdwn', text: `The link works once, for about ${hours} hour${hours === 1 ? '' : 's'}. Your agents get access after you allow it.` }],
37
+ },
38
+ ],
39
+ };
40
+ }
41
+ /**
42
+ * Open the owner DM on a bot and post.
43
+ *
44
+ * @param fetchImpl - fetch
45
+ * @param botToken - Bot token (never logged)
46
+ * @param ownerUserId - Owner
47
+ * @param message - Text and optional blocks
48
+ * @returns True when Slack accepted it
49
+ */
50
+ async function postDm(fetchImpl, botToken, ownerUserId, message) {
51
+ const call = async (method, body) => {
52
+ const res = await fetchImpl(`${SLACK.SLACK_API_BASE}/${method}`, {
53
+ method: 'POST',
54
+ headers: { Authorization: `Bearer ${botToken}`, 'Content-Type': 'application/json; charset=utf-8' },
55
+ body: JSON.stringify(body),
56
+ signal: AbortSignal.timeout(SLACK.SLACK_REQUEST_TIMEOUT_MS),
57
+ });
58
+ const data = (await res.json().catch(() => ({})));
59
+ if (!res.ok || data['ok'] !== true)
60
+ throw new Error(`Slack ${method} failed: ${String(data['error'] ?? res.status)}`);
61
+ return data;
62
+ };
63
+ const opened = await call('conversations.open', { users: ownerUserId });
64
+ const channel = opened['channel']?.id;
65
+ if (!channel)
66
+ throw new Error('Slack conversations.open returned no channel');
67
+ await call('chat.postMessage', { channel, text: message.text, ...(message.blocks ? { blocks: message.blocks } : {}), unfurl_links: false, unfurl_media: false });
68
+ return true;
69
+ }
70
+ /**
71
+ * The notifier the auth service posts through.
72
+ *
73
+ * @param deps - Owner id, bot token lookup, fetch
74
+ * @returns Notifier
75
+ */
76
+ export function createSlackRemoteMcpNotifier(deps) {
77
+ const fetchImpl = deps.fetchImpl ?? fetch;
78
+ const target = (agentSession) => {
79
+ const owner = deps.ownerUserId();
80
+ const token = deps.botTokenFor(agentSession) ?? (agentSession ? deps.botTokenFor(undefined) : null);
81
+ return owner && token ? { owner, token } : null;
82
+ };
83
+ return {
84
+ async postAuthCard({ serverLabel, url, expiresAt, agentSession }) {
85
+ const t = target(agentSession);
86
+ if (!t)
87
+ return false;
88
+ return postDm(fetchImpl, t.token, t.owner, buildRemoteMcpAuthCard(serverLabel, url, expiresAt));
89
+ },
90
+ async postReceipt({ text, agentSession }) {
91
+ const t = target(agentSession);
92
+ if (!t)
93
+ return false;
94
+ return postDm(fetchImpl, t.token, t.owner, { text });
95
+ },
96
+ };
97
+ }
98
+ //# sourceMappingURL=remote-mcp-auth-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-mcp-auth-card.js","sourceRoot":"","sources":["../../../../../../backend/src/services/connector/remote-mcp-auth-card.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,277 @@
1
+ /**
2
+ * OAuth for remote MCP servers — the owner signs in once, from a phone.
3
+ *
4
+ * Flow (owner-away: nothing runs on `localhost` in the owner's browser):
5
+ *
6
+ * 1. A server answers 401 + `WWW-Authenticate: Bearer resource_metadata=…`
7
+ * (on add, on test, or through the agent proxy). Discovery, dynamic
8
+ * client registration and PKCE run here (see remote-mcp-oauth).
9
+ * 2. The authorize URL (minus `state`) goes to the Crewly Cloud broker,
10
+ * which adds `state`, hosts the public redirect URI and returns a
11
+ * single-use ticket link. The PKCE verifier never leaves this machine.
12
+ * 3. The link reaches the owner as a Slack card (throttled), in the API
13
+ * response and on the Connections / portal card.
14
+ * 4. This service polls the broker; when the code arrives it exchanges it
15
+ * with the verifier, stores the tokens and tells the broker (the page on
16
+ * the owner's phone then says "connected") and Slack.
17
+ * 5. {@link RemoteMcpAuthService.getAccessToken} hands the agent proxy a
18
+ * fresh token, refreshing before expiry (single-flight per server).
19
+ *
20
+ * State lives in `<CREWLY_HOME>/remote-mcp-oauth.enc`: AES-256-GCM under
21
+ * the install's master key, mode 0600. Nothing secret is logged.
22
+ *
23
+ * @module services/connector/remote-mcp-auth.service
24
+ */
25
+ import { type BearerChallenge, type DiscoveredAuthorization, type OAuthClient, type OAuthFetch, type OAuthTokens } from './remote-mcp-oauth.js';
26
+ import { type RemoteMcpServer } from './remote-mcp.service.js';
27
+ /** A sign-in waiting on the owner. */
28
+ export interface PendingAuthorization {
29
+ /** Broker session (never logged). */
30
+ state: string;
31
+ verifier: string;
32
+ /** Ticket link (single-use). */
33
+ url: string;
34
+ startedAt: number;
35
+ /** When the link stops working. */
36
+ expiresAt: number;
37
+ /** The owner has opened the link. */
38
+ opened?: boolean;
39
+ /** The agent whose call needed it (its Slack DM gets the receipt). */
40
+ agentSession?: string;
41
+ }
42
+ /** Per-server OAuth record. */
43
+ export interface RemoteMcpOAuthRecord {
44
+ status: 'needs_auth' | 'connected' | 'error';
45
+ discovered?: DiscoveredAuthorization;
46
+ client?: OAuthClient;
47
+ /** The client was entered by the owner (no dynamic registration). */
48
+ manualClient?: boolean;
49
+ tokens?: OAuthTokens;
50
+ connectedAt?: string;
51
+ lastError?: string;
52
+ pending?: PendingAuthorization;
53
+ /** Last Slack card: when, and for which link. */
54
+ lastCard?: {
55
+ at: number;
56
+ state: string;
57
+ };
58
+ }
59
+ /** What the API shows about a server's sign-in (no secrets). */
60
+ export interface RemoteMcpAuthView {
61
+ mode: 'oauth';
62
+ status: 'needs_auth' | 'connected' | 'error';
63
+ /** Host of the authorization server. */
64
+ authorizationServer?: string;
65
+ scopes?: string[];
66
+ /** ISO time the current access token expires. */
67
+ expiresAt?: string;
68
+ connectedAt?: string;
69
+ error?: string;
70
+ /** A live sign-in link, when one is pending. */
71
+ authorizeUrl?: string;
72
+ authorizeExpiresAt?: string;
73
+ }
74
+ /** Slack side, wired at boot (see slack-initializer). */
75
+ export interface RemoteMcpAuthNotifier {
76
+ /** Post the "sign in once" card to the owner. */
77
+ postAuthCard(args: {
78
+ serverLabel: string;
79
+ url: string;
80
+ expiresAt: string;
81
+ agentSession?: string;
82
+ }): Promise<boolean>;
83
+ /** Tell the owner how it went. */
84
+ postReceipt(args: {
85
+ serverLabel: string;
86
+ text: string;
87
+ agentSession?: string;
88
+ }): Promise<boolean>;
89
+ }
90
+ /** Collaborators (tests inject). */
91
+ export interface RemoteMcpAuthDeps {
92
+ crewlyHome?: string;
93
+ fetchImpl?: OAuthFetch;
94
+ cloud?: {
95
+ getToken(): string | null;
96
+ getCloudUrl(): string | null;
97
+ };
98
+ now?: () => number;
99
+ /** Schedules a poll (tests drive it by hand). */
100
+ setTimer?: (fn: () => void, ms: number) => {
101
+ unref?: () => void;
102
+ } | unknown;
103
+ clearTimer?: (handle: unknown) => void;
104
+ /** Server lookup (label, URL) for polls that outlive the caller. */
105
+ getServer?: (id: string) => Promise<RemoteMcpServer | undefined>;
106
+ /** List tools after sign-in (for the receipt). */
107
+ countTools?: (server: RemoteMcpServer, headers: Record<string, string>) => Promise<number | null>;
108
+ }
109
+ /** Owner-facing failure of an authorization start. */
110
+ export declare class RemoteMcpAuthError extends Error {
111
+ readonly code: 'not_signed_in' | 'cloud_error' | 'oauth' | 'not_oauth';
112
+ constructor(code: 'not_signed_in' | 'cloud_error' | 'oauth' | 'not_oauth', message: string);
113
+ }
114
+ /**
115
+ * Wire (or clear) the Slack notifier.
116
+ *
117
+ * @param next - Notifier, or null
118
+ */
119
+ export declare function setRemoteMcpAuthNotifier(next: RemoteMcpAuthNotifier | null): void;
120
+ /**
121
+ * Remote MCP OAuth — see module docs.
122
+ */
123
+ export declare class RemoteMcpAuthService {
124
+ private static instance;
125
+ private readonly logger;
126
+ private readonly crewlyHome;
127
+ private readonly fetchImpl;
128
+ private readonly cloud;
129
+ private readonly now;
130
+ private readonly setTimer;
131
+ private readonly clearTimer;
132
+ private readonly deps;
133
+ private cache;
134
+ private writeChain;
135
+ private readonly refreshing;
136
+ private readonly timers;
137
+ private redirectUri;
138
+ private readonly lastUnauthorized;
139
+ constructor(deps?: RemoteMcpAuthDeps);
140
+ static getInstance(): RemoteMcpAuthService;
141
+ /** Reset the singleton (tests). */
142
+ static resetInstance(): void;
143
+ /** Install an instance (tests / wiring). */
144
+ static setInstance(service: RemoteMcpAuthService): void;
145
+ /** Absolute path of the encrypted store. */
146
+ getFilePath(): string;
147
+ private masterKeyPath;
148
+ /** Stop every poll timer. */
149
+ stop(): void;
150
+ private load;
151
+ private mutate;
152
+ /**
153
+ * The record for a server, if it uses OAuth.
154
+ *
155
+ * @param id - Server id
156
+ * @returns A copy of the record, or undefined
157
+ */
158
+ getRecord(id: string): Promise<RemoteMcpOAuthRecord | undefined>;
159
+ /**
160
+ * Whether a server signs in with OAuth.
161
+ *
162
+ * @param id - Server id
163
+ * @returns True when it has an OAuth record
164
+ */
165
+ usesOAuth(id: string): Promise<boolean>;
166
+ /**
167
+ * The API view of a server's sign-in.
168
+ *
169
+ * @param id - Server id
170
+ * @returns The view, or undefined for a static-key server
171
+ */
172
+ view(id: string): Promise<RemoteMcpAuthView | undefined>;
173
+ /**
174
+ * Forget a server's sign-in (server removed).
175
+ *
176
+ * @param id - Server id
177
+ */
178
+ forget(id: string): Promise<void>;
179
+ /**
180
+ * Set an owner-entered client (servers without dynamic registration).
181
+ *
182
+ * @param id - Server id
183
+ * @param client - Client id and optional secret
184
+ */
185
+ setManualClient(id: string, client: {
186
+ clientId: string;
187
+ clientSecret?: string;
188
+ }): Promise<void>;
189
+ /**
190
+ * Ask the server (no credentials beyond its static headers) whether it
191
+ * wants OAuth.
192
+ *
193
+ * @param server - Stored server
194
+ * @returns The Bearer challenge when it answered 401, else null
195
+ */
196
+ detect(server: RemoteMcpServer): Promise<BearerChallenge | null>;
197
+ /**
198
+ * Record that a server needs OAuth and run discovery.
199
+ *
200
+ * @param server - Stored server
201
+ * @param challenge - What its 401 said
202
+ * @returns The record
203
+ * @throws RemoteMcpAuthError oauth (discovery failed)
204
+ */
205
+ markOAuth(server: RemoteMcpServer, challenge: BearerChallenge | null): Promise<RemoteMcpOAuthRecord>;
206
+ private cloudRequest;
207
+ private getRedirectUri;
208
+ /**
209
+ * Start (or reuse) a sign-in and return the link for the owner.
210
+ *
211
+ * @param server - Stored server
212
+ * @param options - Who needed it, and whether to post the Slack card
213
+ * @returns The ticket link and its expiry
214
+ * @throws RemoteMcpAuthError
215
+ */
216
+ startAuthorization(server: RemoteMcpServer, options?: {
217
+ agentSession?: string;
218
+ notify?: boolean;
219
+ challenge?: BearerChallenge | null;
220
+ }): Promise<{
221
+ url: string;
222
+ expiresAt: string;
223
+ posted: boolean;
224
+ }>;
225
+ /**
226
+ * Post the Slack card unless one carrying this same live link went out
227
+ * within the throttle window.
228
+ *
229
+ * @param server - Server
230
+ * @param pending - The live sign-in
231
+ * @param agentSession - Agent whose DM should carry it
232
+ * @returns Whether a card was posted
233
+ */
234
+ private maybePostCard;
235
+ /**
236
+ * The proxy got a 401 it could not fix: ask the owner (throttled).
237
+ *
238
+ * @param server - Server
239
+ * @param agentSession - The calling agent
240
+ */
241
+ onUnauthorized(server: RemoteMcpServer, agentSession?: string): Promise<void>;
242
+ /**
243
+ * Resume polls for sign-ins in flight (boot).
244
+ */
245
+ resumePending(): Promise<void>;
246
+ private schedulePoll;
247
+ /**
248
+ * One broker poll for a server's pending sign-in (exposed for tests).
249
+ *
250
+ * @param id - Server id
251
+ * @returns The broker status seen
252
+ */
253
+ pollOnce(id: string): Promise<string>;
254
+ private clearPending;
255
+ /**
256
+ * Exchange the code and store the tokens; tell Cloud and Slack.
257
+ *
258
+ * @param id - Server id
259
+ * @param pending - The sign-in
260
+ * @param code - Authorization code
261
+ */
262
+ private complete;
263
+ private receipt;
264
+ /**
265
+ * A usable access token for a server, refreshed when it is about to
266
+ * expire (or when `force`).
267
+ *
268
+ * @param id - Server id
269
+ * @param options - `force` refreshes even when the token looks fresh
270
+ * @returns The access token, or null when the owner must sign in
271
+ */
272
+ getAccessToken(id: string, options?: {
273
+ force?: boolean;
274
+ }): Promise<string | null>;
275
+ private refresh;
276
+ }
277
+ //# sourceMappingURL=remote-mcp-auth.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-mcp-auth.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/connector/remote-mcp-auth.service.ts"],"names":[],"mappings":"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"}