@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.
package/.agents/cli-api.md
CHANGED
|
@@ -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
|
@@ -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
|
|
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
|
|
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
|
|
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);
|