agent-slack 0.5.1 → 0.5.2
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/README.md +2 -2
- package/dist/index.js +124 -16
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -95,10 +95,10 @@ Notes:
|
|
|
95
95
|
|
|
96
96
|
## Authentication (no fancy setup)
|
|
97
97
|
|
|
98
|
-
On macOS, authentication happens automatically:
|
|
98
|
+
On macOS and Windows, authentication happens automatically:
|
|
99
99
|
|
|
100
100
|
- Default: reads Slack Desktop local data (no need to quit Slack)
|
|
101
|
-
- Fallbacks: if that fails, tries Chrome/Firefox extraction
|
|
101
|
+
- Fallbacks: if that fails, tries Chrome/Firefox extraction (macOS)
|
|
102
102
|
|
|
103
103
|
You can also run manual imports:
|
|
104
104
|
|
package/dist/index.js
CHANGED
|
@@ -513,10 +513,17 @@ function parseSlackCurlCommand(curlInput) {
|
|
|
513
513
|
|
|
514
514
|
// src/auth/desktop.ts
|
|
515
515
|
import { cp, mkdir, rm as rm2, unlink } from "node:fs/promises";
|
|
516
|
-
import {
|
|
516
|
+
import {
|
|
517
|
+
existsSync as existsSync4,
|
|
518
|
+
readFileSync as readFileSync2,
|
|
519
|
+
readdirSync,
|
|
520
|
+
copyFileSync,
|
|
521
|
+
writeFileSync,
|
|
522
|
+
unlinkSync
|
|
523
|
+
} from "node:fs";
|
|
517
524
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
518
525
|
import { pbkdf2Sync as pbkdf2Sync2, createDecipheriv as createDecipheriv2 } from "node:crypto";
|
|
519
|
-
import { homedir as homedir3, platform as platform4 } from "node:os";
|
|
526
|
+
import { homedir as homedir3, platform as platform4, tmpdir as tmpdir2 } from "node:os";
|
|
520
527
|
import { join as join5 } from "node:path";
|
|
521
528
|
|
|
522
529
|
// src/lib/leveldb-reader.ts
|
|
@@ -819,12 +826,38 @@ async function queryReadonlySqlite2(dbPath, sql) {
|
|
|
819
826
|
var PLATFORM2 = platform4();
|
|
820
827
|
var IS_MACOS4 = PLATFORM2 === "darwin";
|
|
821
828
|
var IS_LINUX2 = PLATFORM2 === "linux";
|
|
829
|
+
var IS_WIN32 = PLATFORM2 === "win32";
|
|
822
830
|
var SLACK_SUPPORT_DIR_ELECTRON = join5(homedir3(), "Library", "Application Support", "Slack");
|
|
823
831
|
var SLACK_SUPPORT_DIR_APPSTORE = join5(homedir3(), "Library", "Containers", "com.tinyspeck.slackmacgap", "Data", "Library", "Application Support", "Slack");
|
|
824
832
|
var SLACK_SUPPORT_DIR_LINUX = join5(homedir3(), ".config", "Slack");
|
|
825
833
|
var SLACK_SUPPORT_DIR_LINUX_FLATPAK = join5(homedir3(), ".var", "app", "com.slack.Slack", "config", "Slack");
|
|
834
|
+
var SLACK_SUPPORT_DIR_WIN_APPDATA = join5(process.env.APPDATA || join5(homedir3(), "AppData", "Roaming"), "Slack");
|
|
835
|
+
function getWindowsStoreSlackPath() {
|
|
836
|
+
const pkgBase = join5(process.env.LOCALAPPDATA || join5(homedir3(), "AppData", "Local"), "Packages");
|
|
837
|
+
try {
|
|
838
|
+
const entries = readdirSync(pkgBase);
|
|
839
|
+
const slackPkg = entries.find((e) => e.startsWith("com.tinyspeck.slackdesktop_"));
|
|
840
|
+
if (slackPkg) {
|
|
841
|
+
return join5(pkgBase, slackPkg, "LocalCache", "Roaming", "Slack");
|
|
842
|
+
}
|
|
843
|
+
} catch {}
|
|
844
|
+
return null;
|
|
845
|
+
}
|
|
826
846
|
function getSlackPaths() {
|
|
827
|
-
|
|
847
|
+
let candidates;
|
|
848
|
+
if (IS_MACOS4) {
|
|
849
|
+
candidates = [SLACK_SUPPORT_DIR_ELECTRON, SLACK_SUPPORT_DIR_APPSTORE];
|
|
850
|
+
} else if (IS_LINUX2) {
|
|
851
|
+
candidates = [SLACK_SUPPORT_DIR_LINUX_FLATPAK, SLACK_SUPPORT_DIR_LINUX];
|
|
852
|
+
} else if (IS_WIN32) {
|
|
853
|
+
candidates = [SLACK_SUPPORT_DIR_WIN_APPDATA];
|
|
854
|
+
const storePath = getWindowsStoreSlackPath();
|
|
855
|
+
if (storePath) {
|
|
856
|
+
candidates.push(storePath);
|
|
857
|
+
}
|
|
858
|
+
} else {
|
|
859
|
+
candidates = [];
|
|
860
|
+
}
|
|
828
861
|
if (candidates.length === 0) {
|
|
829
862
|
throw new Error(`Slack Desktop extraction is not supported on ${PLATFORM2}.`);
|
|
830
863
|
}
|
|
@@ -833,7 +866,7 @@ function getSlackPaths() {
|
|
|
833
866
|
if (existsSync4(leveldbDir)) {
|
|
834
867
|
const cookiesDbCandidates = [join5(dir, "Network", "Cookies"), join5(dir, "Cookies")];
|
|
835
868
|
const cookiesDb = cookiesDbCandidates.find((candidate) => existsSync4(candidate)) || cookiesDbCandidates[0];
|
|
836
|
-
return { leveldbDir, cookiesDb };
|
|
869
|
+
return { leveldbDir, cookiesDb, baseDir: dir };
|
|
837
870
|
}
|
|
838
871
|
}
|
|
839
872
|
throw new Error(`Slack Desktop data not found. Checked:
|
|
@@ -1032,11 +1065,74 @@ function decryptChromiumCookieValue2(data, password) {
|
|
|
1032
1065
|
return rawToken;
|
|
1033
1066
|
}
|
|
1034
1067
|
}
|
|
1035
|
-
|
|
1068
|
+
function decryptCookieWindows(encrypted, slackDataDir) {
|
|
1069
|
+
const localStatePath = join5(slackDataDir, "Local State");
|
|
1070
|
+
if (!existsSync4(localStatePath)) {
|
|
1071
|
+
throw new Error(`Local State file not found: ${localStatePath}`);
|
|
1072
|
+
}
|
|
1073
|
+
const localState = JSON.parse(readFileSync2(localStatePath, "utf8"));
|
|
1074
|
+
if (!localState.os_crypt?.encrypted_key) {
|
|
1075
|
+
throw new Error("No os_crypt.encrypted_key in Local State");
|
|
1076
|
+
}
|
|
1077
|
+
const encKeyFull = Buffer.from(localState.os_crypt.encrypted_key, "base64");
|
|
1078
|
+
const encKeyBlob = encKeyFull.subarray(5);
|
|
1079
|
+
const encKeyFile = join5(tmpdir2(), `as-key-enc-${Date.now()}.bin`);
|
|
1080
|
+
const decKeyFile = join5(tmpdir2(), `as-key-dec-${Date.now()}.bin`);
|
|
1081
|
+
writeFileSync(encKeyFile, encKeyBlob);
|
|
1082
|
+
try {
|
|
1083
|
+
const psEncKeyFile = encKeyFile.replaceAll("'", "''");
|
|
1084
|
+
const psDecKeyFile = decKeyFile.replaceAll("'", "''");
|
|
1085
|
+
const psCmd = [
|
|
1086
|
+
"Add-Type -AssemblyName System.Security",
|
|
1087
|
+
`$e=[System.IO.File]::ReadAllBytes('${psEncKeyFile}')`,
|
|
1088
|
+
"$d=[System.Security.Cryptography.ProtectedData]::Unprotect($e,$null,[System.Security.Cryptography.DataProtectionScope]::CurrentUser)",
|
|
1089
|
+
`[System.IO.File]::WriteAllBytes('${psDecKeyFile}',$d)`
|
|
1090
|
+
].join("; ");
|
|
1091
|
+
execFileSync2("powershell", ["-ExecutionPolicy", "Bypass", "-Command", psCmd], {
|
|
1092
|
+
stdio: "pipe"
|
|
1093
|
+
});
|
|
1094
|
+
if (!existsSync4(decKeyFile)) {
|
|
1095
|
+
throw new Error("DPAPI decryption failed: PowerShell did not produce the decrypted key file");
|
|
1096
|
+
}
|
|
1097
|
+
const aesKey = readFileSync2(decKeyFile);
|
|
1098
|
+
const nonce = encrypted.subarray(3, 15);
|
|
1099
|
+
const ciphertextWithTag = encrypted.subarray(15);
|
|
1100
|
+
const tag = ciphertextWithTag.subarray(-16);
|
|
1101
|
+
const ciphertext = ciphertextWithTag.subarray(0, -16);
|
|
1102
|
+
const decipher = createDecipheriv2("aes-256-gcm", aesKey, nonce);
|
|
1103
|
+
decipher.setAuthTag(tag);
|
|
1104
|
+
let decrypted = decipher.update(ciphertext, undefined, "utf8");
|
|
1105
|
+
decrypted += decipher.final("utf8");
|
|
1106
|
+
return decrypted;
|
|
1107
|
+
} finally {
|
|
1108
|
+
try {
|
|
1109
|
+
unlinkSync(encKeyFile);
|
|
1110
|
+
} catch {}
|
|
1111
|
+
try {
|
|
1112
|
+
unlinkSync(decKeyFile);
|
|
1113
|
+
} catch {}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
async function extractCookieDFromSlackCookiesDb(cookiesPath, slackDataDir) {
|
|
1036
1117
|
if (!existsSync4(cookiesPath)) {
|
|
1037
1118
|
throw new Error(`Slack Cookies DB not found: ${cookiesPath}`);
|
|
1038
1119
|
}
|
|
1039
|
-
|
|
1120
|
+
let dbPathToQuery = cookiesPath;
|
|
1121
|
+
if (IS_WIN32) {
|
|
1122
|
+
const tmpCopy = join5(tmpdir2(), `agent-slack-cookies-${Date.now()}`);
|
|
1123
|
+
copyFileSync(cookiesPath, tmpCopy);
|
|
1124
|
+
dbPathToQuery = tmpCopy;
|
|
1125
|
+
}
|
|
1126
|
+
let rows;
|
|
1127
|
+
try {
|
|
1128
|
+
rows = await queryReadonlySqlite2(dbPathToQuery, "select host_key, name, value, encrypted_value from cookies where name = 'd' and host_key like '%slack.com' order by length(encrypted_value) desc");
|
|
1129
|
+
} finally {
|
|
1130
|
+
if (IS_WIN32 && dbPathToQuery !== cookiesPath) {
|
|
1131
|
+
try {
|
|
1132
|
+
unlinkSync(dbPathToQuery);
|
|
1133
|
+
} catch {}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1040
1136
|
if (!rows || rows.length === 0) {
|
|
1041
1137
|
throw new Error("No Slack 'd' cookie found");
|
|
1042
1138
|
}
|
|
@@ -1049,6 +1145,18 @@ async function extractCookieDFromSlackCookiesDb(cookiesPath) {
|
|
|
1049
1145
|
throw new Error("Slack 'd' cookie had no encrypted_value");
|
|
1050
1146
|
}
|
|
1051
1147
|
const prefix = encrypted.subarray(0, 3).toString("utf8");
|
|
1148
|
+
if (IS_WIN32 && (prefix === "v10" || prefix === "v11")) {
|
|
1149
|
+
const decrypted = decryptCookieWindows(encrypted, slackDataDir);
|
|
1150
|
+
const match = decrypted.match(/xoxd-[A-Za-z0-9%/+_=.-]+/);
|
|
1151
|
+
if (match) {
|
|
1152
|
+
try {
|
|
1153
|
+
return decodeURIComponent(match[0]);
|
|
1154
|
+
} catch {
|
|
1155
|
+
return match[0];
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
throw new Error("Could not locate xoxd-* in DPAPI-decrypted Slack cookie");
|
|
1159
|
+
}
|
|
1052
1160
|
const data = prefix === "v10" || prefix === "v11" ? encrypted.subarray(3) : encrypted;
|
|
1053
1161
|
const passwords = getSafeStoragePasswords2(prefix);
|
|
1054
1162
|
for (const password of passwords) {
|
|
@@ -1063,9 +1171,9 @@ async function extractCookieDFromSlackCookiesDb(cookiesPath) {
|
|
|
1063
1171
|
throw new Error("Could not locate xoxd-* in decrypted Slack cookie");
|
|
1064
1172
|
}
|
|
1065
1173
|
async function extractFromSlackDesktop() {
|
|
1066
|
-
const { leveldbDir, cookiesDb } = getSlackPaths();
|
|
1174
|
+
const { leveldbDir, cookiesDb, baseDir } = getSlackPaths();
|
|
1067
1175
|
const teams = await extractTeamsFromSlackLevelDb(leveldbDir);
|
|
1068
|
-
const cookie_d = await extractCookieDFromSlackCookiesDb(cookiesDb);
|
|
1176
|
+
const cookie_d = await extractCookieDFromSlackCookiesDb(cookiesDb, baseDir);
|
|
1069
1177
|
return {
|
|
1070
1178
|
cookie_d,
|
|
1071
1179
|
teams,
|
|
@@ -2025,7 +2133,7 @@ function isAuthErrorMessage(message) {
|
|
|
2025
2133
|
return /(?:^|[^a-z])(invalid_auth|token_expired)(?:$|[^a-z])/i.test(message);
|
|
2026
2134
|
}
|
|
2027
2135
|
async function refreshFromDesktopIfPossible() {
|
|
2028
|
-
if (process.platform !== "darwin" && process.platform !== "linux") {
|
|
2136
|
+
if (process.platform !== "darwin" && process.platform !== "linux" && process.platform !== "win32") {
|
|
2029
2137
|
return false;
|
|
2030
2138
|
}
|
|
2031
2139
|
try {
|
|
@@ -2405,7 +2513,7 @@ import { join as join10, resolve as resolve2 } from "node:path";
|
|
|
2405
2513
|
import { mkdir as mkdir4 } from "node:fs/promises";
|
|
2406
2514
|
|
|
2407
2515
|
// src/lib/app-dir.ts
|
|
2408
|
-
import { homedir as homedir5, tmpdir as
|
|
2516
|
+
import { homedir as homedir5, tmpdir as tmpdir3 } from "node:os";
|
|
2409
2517
|
import { join as join9 } from "node:path";
|
|
2410
2518
|
function getAppDir() {
|
|
2411
2519
|
const xdg = process.env.XDG_RUNTIME_DIR?.trim();
|
|
@@ -2416,7 +2524,7 @@ function getAppDir() {
|
|
|
2416
2524
|
if (home) {
|
|
2417
2525
|
return join9(home, ".agent-slack");
|
|
2418
2526
|
}
|
|
2419
|
-
return join9(
|
|
2527
|
+
return join9(tmpdir3(), "agent-slack");
|
|
2420
2528
|
}
|
|
2421
2529
|
|
|
2422
2530
|
// src/lib/tmp-paths.ts
|
|
@@ -4142,7 +4250,7 @@ async function reactOnTarget(input) {
|
|
|
4142
4250
|
// src/cli/draft-server.ts
|
|
4143
4251
|
import { createServer } from "node:http";
|
|
4144
4252
|
import { execFile } from "node:child_process";
|
|
4145
|
-
import { readFileSync as
|
|
4253
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
4146
4254
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
4147
4255
|
import { dirname as dirname3, join as join12 } from "node:path";
|
|
4148
4256
|
function openDraftEditor(config) {
|
|
@@ -4277,7 +4385,7 @@ function buildEditorHtml(config) {
|
|
|
4277
4385
|
var _editorHtml;
|
|
4278
4386
|
function getEditorHtml() {
|
|
4279
4387
|
if (!_editorHtml) {
|
|
4280
|
-
_editorHtml =
|
|
4388
|
+
_editorHtml = readFileSync3(join12(dirname3(fileURLToPath2(import.meta.url)), "draft-editor.html"), "utf-8");
|
|
4281
4389
|
}
|
|
4282
4390
|
return _editorHtml;
|
|
4283
4391
|
}
|
|
@@ -5207,7 +5315,7 @@ function registerSearchCommand(input) {
|
|
|
5207
5315
|
import { execSync as execSync2 } from "node:child_process";
|
|
5208
5316
|
import { createHash } from "node:crypto";
|
|
5209
5317
|
import { chmod, copyFile as copyFile2, mkdir as mkdir5, readFile as readFile7, rename, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
|
|
5210
|
-
import { tmpdir as
|
|
5318
|
+
import { tmpdir as tmpdir4 } from "node:os";
|
|
5211
5319
|
import { basename as basename3, join as join13 } from "node:path";
|
|
5212
5320
|
var REPO = "stablyai/agent-slack";
|
|
5213
5321
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
@@ -5314,7 +5422,7 @@ async function performUpdate(latest) {
|
|
|
5314
5422
|
const asset = detectPlatformAsset();
|
|
5315
5423
|
const tag = `v${latest}`;
|
|
5316
5424
|
const baseUrl = `https://github.com/${REPO}/releases/download/${tag}`;
|
|
5317
|
-
const tmp = join13(
|
|
5425
|
+
const tmp = join13(tmpdir4(), `agent-slack-update-${Date.now()}`);
|
|
5318
5426
|
await mkdir5(tmp, { recursive: true });
|
|
5319
5427
|
const binTmp = join13(tmp, asset);
|
|
5320
5428
|
const sumsTmp = join13(tmp, "checksums-sha256.txt");
|
|
@@ -5878,5 +5986,5 @@ if (subcommand && subcommand !== "update") {
|
|
|
5878
5986
|
backgroundUpdateCheck();
|
|
5879
5987
|
}
|
|
5880
5988
|
|
|
5881
|
-
//# debugId=
|
|
5989
|
+
//# debugId=1E9C1FBA7D38320464756E2164756E21
|
|
5882
5990
|
//# sourceMappingURL=index.js.map
|