agent-slack 0.5.0 → 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 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 { existsSync as existsSync4 } from "node:fs";
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
- const candidates = IS_MACOS4 ? [SLACK_SUPPORT_DIR_ELECTRON, SLACK_SUPPORT_DIR_APPSTORE] : IS_LINUX2 ? [SLACK_SUPPORT_DIR_LINUX_FLATPAK, SLACK_SUPPORT_DIR_LINUX] : [];
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
- async function extractCookieDFromSlackCookiesDb(cookiesPath) {
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
- const rows = await queryReadonlySqlite2(cookiesPath, "select host_key, name, value, encrypted_value from cookies where name = 'd' and host_key like '%slack.com' order by length(encrypted_value) desc");
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 tmpdir2 } from "node:os";
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(tmpdir2(), "agent-slack");
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 readFileSync2 } from "node:fs";
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) {
@@ -4272,9 +4380,15 @@ function buildEditorHtml(config) {
4272
4380
  initialText: config.initialText || ""
4273
4381
  });
4274
4382
  const safeConfig = injectedConfig.replace(/</g, "\\u003c").replace(/>/g, "\\u003e");
4275
- return EDITOR_HTML.replace("__DRAFT_CONFIG__", safeConfig);
4383
+ return getEditorHtml().replace("__DRAFT_CONFIG__", safeConfig);
4384
+ }
4385
+ var _editorHtml;
4386
+ function getEditorHtml() {
4387
+ if (!_editorHtml) {
4388
+ _editorHtml = readFileSync3(join12(dirname3(fileURLToPath2(import.meta.url)), "draft-editor.html"), "utf-8");
4389
+ }
4390
+ return _editorHtml;
4276
4391
  }
4277
- var EDITOR_HTML = readFileSync2(join12(dirname3(fileURLToPath2(import.meta.url)), "draft-editor.html"), "utf-8");
4278
4392
 
4279
4393
  // src/cli/draft-actions.ts
4280
4394
  async function draftMessage(input) {
@@ -5201,7 +5315,7 @@ function registerSearchCommand(input) {
5201
5315
  import { execSync as execSync2 } from "node:child_process";
5202
5316
  import { createHash } from "node:crypto";
5203
5317
  import { chmod, copyFile as copyFile2, mkdir as mkdir5, readFile as readFile7, rename, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
5204
- import { tmpdir as tmpdir3 } from "node:os";
5318
+ import { tmpdir as tmpdir4 } from "node:os";
5205
5319
  import { basename as basename3, join as join13 } from "node:path";
5206
5320
  var REPO = "stablyai/agent-slack";
5207
5321
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
@@ -5308,7 +5422,7 @@ async function performUpdate(latest) {
5308
5422
  const asset = detectPlatformAsset();
5309
5423
  const tag = `v${latest}`;
5310
5424
  const baseUrl = `https://github.com/${REPO}/releases/download/${tag}`;
5311
- const tmp = join13(tmpdir3(), `agent-slack-update-${Date.now()}`);
5425
+ const tmp = join13(tmpdir4(), `agent-slack-update-${Date.now()}`);
5312
5426
  await mkdir5(tmp, { recursive: true });
5313
5427
  const binTmp = join13(tmp, asset);
5314
5428
  const sumsTmp = join13(tmp, "checksums-sha256.txt");
@@ -5872,5 +5986,5 @@ if (subcommand && subcommand !== "update") {
5872
5986
  backgroundUpdateCheck();
5873
5987
  }
5874
5988
 
5875
- //# debugId=3656E716543A3F7A64756E2164756E21
5989
+ //# debugId=1E9C1FBA7D38320464756E2164756E21
5876
5990
  //# sourceMappingURL=index.js.map