@eliya-oss/agent-slack 0.1.8 → 0.1.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.
@@ -55,6 +55,7 @@ Auth behavior:
55
55
  - Browser login uses Agent Slack's public Slack app.
56
56
  - Development and self-hosted OAuth can use Slack app credentials. The **Client ID** and **Client Secret** come from the Slack app's **Basic Information > App Credentials** section.
57
57
  - OAuth opens the Slack OAuth URL in the default browser by default. Use `--no-open` to print the URL only, or `--auth-url-out PATH` for headless agents.
58
+ - Distributed Slack apps use `https://aslk.vercel.app/oauth/slack/callback` as the HTTPS relay redirect URI. The relay forwards Slack's `code` and `state` to the local callback.
58
59
  - PKCE uses `user_scope=...`; OAuth with app credentials uses `scope=...` for bot scopes.
59
60
  - Default local callback: `http://localhost:45454/oauth/slack/callback`.
60
61
  - Stores profiles outside the project directory. The default store is `~/.config/agent-slack/profiles.json`; set `AGENT_SLACK_TOKEN_STORE=keychain` on macOS to keep token secrets in Keychain and only profile metadata on disk.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @eliya-oss/agent-slack
2
2
 
3
+ ## 0.1.10
4
+
5
+ ### Patch Changes
6
+
7
+ - Use the hosted Agent Slack OAuth relay for default browser login.
8
+
9
+ ## 0.1.9
10
+
11
+ ### Patch Changes
12
+
13
+ - Support hosted HTTPS OAuth redirect relays for distributed Slack apps.
14
+
3
15
  ## 0.1.8
4
16
 
5
17
  ### Patch Changes
@@ -15,10 +15,14 @@ export class NodeLocalhostOAuthFlow {
15
15
  const redirectHost = env.AGENT_SLACK_OAUTH_REDIRECT_HOST ?? env.SLK_OAUTH_REDIRECT_HOST;
16
16
  const redirectPort = env.AGENT_SLACK_OAUTH_REDIRECT_PORT ?? env.SLK_OAUTH_REDIRECT_PORT;
17
17
  const redirectPath = env.AGENT_SLACK_OAUTH_REDIRECT_PATH ?? env.SLK_OAUTH_REDIRECT_PATH;
18
+ const slackRedirectUri = env.AGENT_SLACK_OAUTH_REDIRECT_URI ?? env.AGENT_SLACK_OAUTH_PUBLIC_REDIRECT_URI;
19
+ const localCallbackUri = env.AGENT_SLACK_OAUTH_LOCAL_CALLBACK_URI;
18
20
  const timeoutMs = env.AGENT_SLACK_OAUTH_TIMEOUT_MS ?? env.SLK_OAUTH_TIMEOUT_MS;
19
21
  return new NodeLocalhostOAuthFlow({
20
22
  authorizeUrl: env.AGENT_SLACK_OAUTH_AUTHORIZE_URL ?? env.SLK_SLACK_OAUTH_AUTHORIZE_URL ?? (emulatorBaseUrl === undefined ? "https://slack.com/oauth/v2/authorize" : `${emulatorBaseUrl}/oauth/v2/authorize`),
21
23
  tokenUrl: env.AGENT_SLACK_OAUTH_ACCESS_URL ?? env.SLK_SLACK_OAUTH_ACCESS_URL ?? (apiBaseUrl === undefined ? "https://slack.com/api/oauth.v2.access" : `${apiBaseUrl.replace(/\/?$/, "/")}oauth.v2.access`),
24
+ ...(slackRedirectUri === undefined ? {} : { defaultSlackRedirectUri: slackRedirectUri }),
25
+ ...(localCallbackUri === undefined ? {} : { defaultLocalCallbackUri: localCallbackUri }),
22
26
  ...(redirectHost === undefined ? {} : { defaultRedirectHost: redirectHost }),
23
27
  ...(redirectPort === undefined ? {} : { defaultRedirectPort: Number(redirectPort) }),
24
28
  ...(redirectPath === undefined ? {} : { defaultRedirectPath: redirectPath }),
@@ -30,15 +34,20 @@ export class NodeLocalhostOAuthFlow {
30
34
  const codeVerifier = input.pkce === true ? randomBytes(32).toString("base64url") : undefined;
31
35
  const timeoutMs = input.timeoutMs ?? this.options.defaultTimeoutMs ?? 120_000;
32
36
  const server = createServer();
33
- const started = await listen(server, input.redirectUri, {
37
+ const slackRedirectUri = input.redirectUri ?? this.options.defaultSlackRedirectUri;
38
+ const localCallbackUri = input.localCallbackUri ??
39
+ this.options.defaultLocalCallbackUri ??
40
+ (slackRedirectUri !== undefined && isLocalCallbackUri(slackRedirectUri) ? slackRedirectUri : undefined);
41
+ const started = await listen(server, localCallbackUri, {
34
42
  host: this.options.defaultRedirectHost ?? defaultRedirectHost,
35
43
  port: this.options.defaultRedirectPort ?? defaultRedirectPort,
36
44
  path: this.options.defaultRedirectPath ?? "/oauth/slack/callback"
37
45
  });
46
+ const redirectUri = slackRedirectUri ?? started.redirectUri;
38
47
  const authorizationUrl = buildAuthorizationUrl({
39
48
  authorizeUrl: this.options.authorizeUrl,
40
49
  clientId: input.clientId,
41
- redirectUri: started.redirectUri,
50
+ redirectUri,
42
51
  scopes: input.scopes,
43
52
  userScopes: input.userScopes,
44
53
  state,
@@ -85,7 +94,7 @@ export class NodeLocalhostOAuthFlow {
85
94
  clientId: input.clientId,
86
95
  clientSecret: input.clientSecret,
87
96
  codeVerifier,
88
- redirectUri: started.redirectUri
97
+ redirectUri
89
98
  });
90
99
  const profile = toAuthProfile(input.profileName, access, input.pkce === true ? "user" : "bot");
91
100
  response.writeHead(200, { "content-type": "text/html; charset=utf-8" }).end("<html><body>Slack auth complete. You can close this tab.</body></html>");
@@ -124,6 +133,13 @@ const listen = (server, redirectUri, fallback) => new Promise((resolve, reject)
124
133
  });
125
134
  const defaultRedirectHost = "localhost";
126
135
  const defaultRedirectPort = 45454;
136
+ const isLocalCallbackUri = (value) => {
137
+ const parsed = new URL(value);
138
+ return parsed.protocol === "http:" && (parsed.hostname === "localhost" ||
139
+ parsed.hostname === "127.0.0.1" ||
140
+ parsed.hostname === "[::1]" ||
141
+ parsed.hostname === "::1");
142
+ };
127
143
  const buildAuthorizationUrl = (input) => {
128
144
  const url = new URL(input.authorizeUrl);
129
145
  url.searchParams.set("client_id", input.clientId);
@@ -207,13 +207,14 @@ const authCommand = async (parsed, services) => {
207
207
  const scopes = splitCsv(flagString(parsed, "scopes"));
208
208
  const userScopes = splitCsv(flagString(parsed, "user-scopes"));
209
209
  const pkceUserScopes = userScopes.length > 0 ? userScopes : scopes.length > 0 ? scopes : defaultPkceUserScopes;
210
+ const redirectUri = flagString(parsed, "redirect-uri") ?? (!byoOAuth && clientId === defaultPkceClientId ? defaultPkceRedirectUri : undefined);
210
211
  const profile = await services.oauthFlow.login({
211
212
  profileName,
212
213
  clientId,
213
214
  ...(byoOAuth ? { clientSecret } : { pkce: true }),
214
215
  scopes: byoOAuth ? (scopes.length === 0 ? defaultOAuthScopes : scopes) : [],
215
216
  userScopes: byoOAuth ? userScopes : pkceUserScopes,
216
- redirectUri: flagString(parsed, "redirect-uri"),
217
+ redirectUri,
217
218
  authUrlOut: flagString(parsed, "auth-url-out"),
218
219
  timeoutMs: numberFlag(parsed, "timeout-ms"),
219
220
  openBrowser: !flagBoolean(parsed, "no-open")
@@ -241,6 +242,7 @@ const defaultOAuthScopes = [
241
242
  ];
242
243
  const defaultPkceUserScopes = defaultOAuthScopes;
243
244
  const defaultPkceClientId = "11499810382723.11506074725874";
245
+ const defaultPkceRedirectUri = "https://aslk.vercel.app/oauth/slack/callback";
244
246
  const apiCall = async (parsed, services) => {
245
247
  const method = requirePositional(parsed.positionals, 2, "METHOD");
246
248
  const payload = await parseJsonPayload({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eliya-oss/agent-slack",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Slack context CLI for AI agents.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.9.0",