@eliya-oss/agent-slack 0.1.8 → 0.1.9

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 an 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,11 @@
1
1
  # @eliya-oss/agent-slack
2
2
 
3
+ ## 0.1.9
4
+
5
+ ### Patch Changes
6
+
7
+ - Support hosted HTTPS OAuth redirect relays for distributed Slack apps.
8
+
3
9
  ## 0.1.8
4
10
 
5
11
  ### 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);
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.9",
4
4
  "description": "Slack context CLI for AI agents.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.9.0",