{"_id":"@desplega.ai/slack-mock","_rev":"6-d3fa277dcfc447ca58100d49a52b8bd6","name":"@desplega.ai/slack-mock","dist-tags":{"latest":"0.4.0"},"versions":{"0.1.0":{"name":"@desplega.ai/slack-mock","version":"0.1.0","keywords":["slack","mock","socket-mode","bolt","testing","e2e"],"license":"MIT","_id":"@desplega.ai/slack-mock@0.1.0","maintainers":[{"name":"tarasyarema","email":"tarasyarema@pm.me"},{"name":"3z3","email":"ezequiel.cura@gmail.com"}],"homepage":"https://github.com/desplega-ai/slack-mock#readme","bugs":{"url":"https://github.com/desplega-ai/slack-mock/issues"},"bin":{"slack-mock":"dist/cli.js"},"dist":{"shasum":"27192d1cf8d933c7af9c07fab02da2d239c071a8","tarball":"https://registry.npmjs.org/@desplega.ai/slack-mock/-/slack-mock-0.1.0.tgz","fileCount":35,"integrity":"sha512-CSAS89FNQzfel4YLOj5jpa3MIJBJrvl6f0zfVoE7txyXfenSdw7gFh2eBf+YDjXPba7yjdjlUsWosuBG7U4k/g==","signatures":[{"sig":"MEUCIQC8DsA5z+SVudBuNw+THuaDExlAmZML18No73dtiO2KXgIgLYzM3Hw51IEXZ1haoOActdiNrvuZjV2jZldnCaV/Ibg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":366848},"type":"module","types":"./dist/index.d.ts","engines":{"bun":">=1.2"},"exports":{".":{"bun":"./src/index.ts","types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"ae431a89563cedfc435d226a62d6c6ec712fe40c","scripts":{"demo":"bun scripts/demo.ts","lint":"biome check .","test":"bun test","build":"rm -rf dist && bun build src/index.ts src/cli.ts src/screenshot.ts --target=bun --outdir dist --splitting && tsc -p tsconfig.build.json","start":"bun run src/cli.ts serve","release":"npm publish","lint:fix":"biome check --write .","test:e2e":"SLACK_MOCK_E2E=1 bun test test/agent-swarm.e2e.test.ts","typecheck":"tsc --noEmit","prepublishOnly":"bun run typecheck && bun run lint && bun test && bun run build"},"_npmUser":{"name":"tarasyarema","email":"tarasyarema@pm.me"},"repository":{"url":"git+https://github.com/desplega-ai/slack-mock.git","type":"git"},"_npmVersion":"10.9.8","description":"Mock Slack server (Web API + Socket Mode) for end-to-end testing Slack bots","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"latest","typescript":"^5","@slack/bolt":"^4.6.0","@biomejs/biome":"^2.4.5"},"_npmOperationalInternal":{"tmp":"tmp/slack-mock_0.1.0_1788434573395_0.4260681519860823","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@desplega.ai/slack-mock","version":"0.1.1","keywords":["slack","mock","socket-mode","bolt","testing","e2e"],"license":"MIT","_id":"@desplega.ai/slack-mock@0.1.1","maintainers":[{"name":"tarasyarema","email":"tarasyarema@pm.me"},{"name":"3z3","email":"ezequiel.cura@gmail.com"}],"homepage":"https://github.com/desplega-ai/slack-mock#readme","bugs":{"url":"https://github.com/desplega-ai/slack-mock/issues"},"bin":{"slack-mock":"dist/cli.js"},"dist":{"shasum":"d661eae2fb744138ad775431aa6c274cf3c0f165","tarball":"https://registry.npmjs.org/@desplega.ai/slack-mock/-/slack-mock-0.1.1.tgz","fileCount":35,"integrity":"sha512-N71Ufgh/aJ08X3CQ9q/Fwk1RkuAEMlbCHmtHebBV7T82A3O267TeqXOXutB3lodHxvuwQ7J+ejYq6KRkWMxNYw==","signatures":[{"sig":"MEYCIQC1t6z0ZC+6yQsle6waqeVz9DhMXiCIuOluGJMPs6miuQIhANAyVwmUrlz8Xs1kEoJUih5La8arCaByY/xpY//2eDuh","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIDCSRws3XOJtF2uHq+E/DU2/B3RDncJkVBCwsRe1AufaAiBe9yrUt6kAgxmixV4KEi1EX583Zu0Fb2vsGbtkKn9rNg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@desplega.ai%2fslack-mock@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":397484},"type":"module","types":"./dist/index.d.ts","engines":{"bun":">=1.2"},"exports":{".":{"bun":"./src/index.ts","types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"ea024ffd3c59711ddb6383655ae41f1c66f08950","scripts":{"demo":"bun scripts/demo.ts","lint":"biome check .","test":"bun test","build":"rm -rf dist && bun build src/index.ts src/cli.ts src/screenshot.ts --target=bun --outdir dist --splitting && tsc -p tsconfig.build.json","start":"bun run src/cli.ts serve","release":"npm publish","lint:fix":"biome check --write .","test:e2e":"SLACK_MOCK_E2E=1 bun test test/agent-swarm.e2e.test.ts","typecheck":"tsc --noEmit","prepublishOnly":"bun run typecheck && bun run lint && bun test && bun run build"},"_npmUser":{"name":"tarasyarema","email":"tarasyarema@pm.me"},"repository":{"url":"git+https://github.com/desplega-ai/slack-mock.git","type":"git"},"_npmVersion":"10.9.8","description":"Mock Slack server (Web API + Socket Mode) for end-to-end testing Slack bots","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"latest","typescript":"^5","@slack/bolt":"^4.6.0","@biomejs/biome":"^2.4.5"},"_npmOperationalInternal":{"tmp":"tmp/slack-mock_0.1.1_1788469884455_0.9947034875262935","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@desplega.ai/slack-mock","version":"0.1.2","keywords":["slack","mock","socket-mode","bolt","testing","e2e"],"license":"MIT","_id":"@desplega.ai/slack-mock@0.1.2","maintainers":[{"name":"tarasyarema","email":"tarasyarema@pm.me"},{"name":"3z3","email":"ezequiel.cura@gmail.com"}],"homepage":"https://github.com/desplega-ai/slack-mock#readme","bugs":{"url":"https://github.com/desplega-ai/slack-mock/issues"},"bin":{"slack-mock":"dist/cli.js"},"dist":{"shasum":"40bf6afe063d299584e52eb1230519ef668941d7","tarball":"https://registry.npmjs.org/@desplega.ai/slack-mock/-/slack-mock-0.1.2.tgz","fileCount":35,"integrity":"sha512-WF/RadnVD/EG5HKtO5wJSa1V8rlXfQ6/2JSTnEskuiestlgn/0MDN5mx99TE2BwP2YZuekKct/SSHXREtPGqCw==","signatures":[{"sig":"MEUCIQDt/WLpmpVE8r+kcY1kvZZkt//Bp74D/ELQOMmB0hiO0QIgQLL20ue6V+vWL0EOTA22mL2b1aPwKtG8j2HK0bKUD4k=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@desplega.ai%2fslack-mock@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":399483},"type":"module","types":"./dist/index.d.ts","engines":{"bun":">=1.2"},"exports":{".":{"bun":"./src/index.ts","types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"ea8b3da1b91caadf4139f34eb4f96de3447f51bf","scripts":{"demo":"bun scripts/demo.ts","lint":"biome check .","test":"bun test","build":"rm -rf dist && bun build src/index.ts src/cli.ts src/screenshot.ts --target=bun --outdir dist --splitting && tsc -p tsconfig.build.json","start":"bun run src/cli.ts serve","release":"npm publish","lint:fix":"biome check --write .","test:e2e":"SLACK_MOCK_E2E=1 bun test test/agent-swarm.e2e.test.ts","typecheck":"tsc --noEmit","prepublishOnly":"bun run typecheck && bun run lint && bun test && bun run build"},"_npmUser":{"name":"tarasyarema","email":"tarasyarema@pm.me"},"repository":{"url":"git+https://github.com/desplega-ai/slack-mock.git","type":"git"},"_npmVersion":"10.9.8","description":"Mock Slack server (Web API + Socket Mode) for end-to-end testing Slack bots","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"latest","typescript":"^5","@slack/bolt":"^4.6.0","@biomejs/biome":"^2.4.5"},"_npmOperationalInternal":{"tmp":"tmp/slack-mock_0.1.2_1788473883154_0.6073222552847095","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@desplega.ai/slack-mock","version":"0.2.0","keywords":["slack","mock","socket-mode","bolt","testing","e2e"],"license":"MIT","_id":"@desplega.ai/slack-mock@0.2.0","maintainers":[{"name":"tarasyarema","email":"tarasyarema@pm.me"},{"name":"3z3","email":"ezequiel.cura@gmail.com"}],"homepage":"https://github.com/desplega-ai/slack-mock#readme","bugs":{"url":"https://github.com/desplega-ai/slack-mock/issues"},"bin":{"slack-mock":"dist/cli.js"},"dist":{"shasum":"52fd2c04fdf6652c06d30f788c486f185b767d8c","tarball":"https://registry.npmjs.org/@desplega.ai/slack-mock/-/slack-mock-0.2.0.tgz","fileCount":37,"integrity":"sha512-LLZly6Mfm7MgGnDzBhctGs6kaS2ye4W8clwC9PKZ7jN4bgwtykrF9SEUcgTN4An23fg7r9PwvBhX45HdXSjAsw==","signatures":[{"sig":"MEUCIAUKddFY1/LCgOcsftz24jbJTSFMdUaU4acKKqOEldxAAiEA1VsJTml7uUxbpL+GzNR0KxiyJ7iGmA9+sQJwib+1++Y=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIGRNN7wZek4JdwJ3UeyMVbVN1SR40BlHMXNJMVT6nQTHAiAOAUYt8g8LdAeQJs4WXFVN/dMNNld1rEmhbm1fzO1lpg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@desplega.ai%2fslack-mock@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":416420},"type":"module","types":"./dist/index.d.ts","engines":{"bun":">=1.2"},"exports":{".":{"bun":"./src/index.ts","types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"ceb70616560eb10ef8d580a314f0b6584b3d52b0","scripts":{"demo":"bun scripts/demo.ts","lint":"biome check .","test":"bun test","build":"rm -rf dist && bun build src/index.ts src/cli.ts src/screenshot.ts --target=bun --outdir dist --splitting && tsc -p tsconfig.build.json","start":"bun run src/cli.ts serve","release":"npm publish","lint:fix":"biome check --write .","test:e2e":"SLACK_MOCK_E2E=1 bun test test/agent-swarm.e2e.test.ts","typecheck":"tsc --noEmit","prepublishOnly":"bun run typecheck && bun run lint && bun test && bun run build"},"_npmUser":{"name":"tarasyarema","email":"tarasyarema@pm.me"},"repository":{"url":"git+https://github.com/desplega-ai/slack-mock.git","type":"git"},"_npmVersion":"10.9.8","description":"Mock Slack server (Web API + Socket Mode) for end-to-end testing Slack bots","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"latest","typescript":"^5","@slack/bolt":"^4.6.0","@biomejs/biome":"^2.4.5"},"_npmOperationalInternal":{"tmp":"tmp/slack-mock_0.2.0_1788518955796_0.5353205203241407","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@desplega.ai/slack-mock","version":"0.3.0","keywords":["slack","mock","socket-mode","bolt","testing","e2e"],"license":"MIT","_id":"@desplega.ai/slack-mock@0.3.0","maintainers":[{"name":"tarasyarema","email":"tarasyarema@pm.me"},{"name":"3z3","email":"ezequiel.cura@gmail.com"}],"homepage":"https://github.com/desplega-ai/slack-mock#readme","bugs":{"url":"https://github.com/desplega-ai/slack-mock/issues"},"bin":{"slack-mock":"dist/cli.js"},"dist":{"shasum":"36b08294aea3531e807be4ed7f0917866d55dca1","tarball":"https://registry.npmjs.org/@desplega.ai/slack-mock/-/slack-mock-0.3.0.tgz","fileCount":37,"integrity":"sha512-cQZ99IqWl35yMPF/BwfGLYBYyTbMxSYRXAokOu/THRNkdrI7MxsZX29QG8tGmJNbXvyoFvsLjW0stL99TC7o6Q==","signatures":[{"sig":"MEUCIQCCTx5jS1Q4z7HXKannIeHnUlJEsLL6/tHpJZMYBPKoqAIgUjbQNlnNqE+QiYRKxJ65ssfGCj8Tdu81vvnNyiXxm5M=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCjNzK7ypVdji8cMPZ5fBR50n5R6HaaYh2vex0lH1agrAIgckrtpifiW8EycqiYgG1r2iJLvv+SOdXsHEgHJwtw0Zs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@desplega.ai%2fslack-mock@0.3.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":416710},"type":"module","types":"./dist/index.d.ts","engines":{"bun":">=1.2"},"exports":{".":{"bun":"./src/index.ts","types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"75ee98e59dfcf871a87f1f1e361d0423400da3e0","scripts":{"demo":"bun scripts/demo.ts","lint":"biome check .","test":"bun test","build":"rm -rf dist && bun build src/index.ts src/cli.ts src/screenshot.ts --target=bun --outdir dist --splitting && tsc -p tsconfig.build.json","start":"bun run src/cli.ts serve","release":"npm publish","lint:fix":"biome check --write .","test:e2e":"SLACK_MOCK_E2E=1 bun test test/agent-swarm.e2e.test.ts","typecheck":"tsc --noEmit","prepublishOnly":"bun run typecheck && bun run lint && bun test && bun run build"},"_npmUser":{"name":"tarasyarema","email":"tarasyarema@pm.me"},"repository":{"url":"git+https://github.com/desplega-ai/slack-mock.git","type":"git"},"_npmVersion":"10.9.8","description":"Mock Slack server (Web API + Socket Mode) for end-to-end testing Slack bots","directories":{},"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"latest","typescript":"^5","@slack/bolt":"^4.6.0","@biomejs/biome":"^2.4.5"},"_npmOperationalInternal":{"tmp":"tmp/slack-mock_0.3.0_1788976352486_0.04863431940427465","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"_id":"@desplega.ai/slack-mock@0.4.0","bin":{"slack-mock":"dist/cli.js"},"bugs":{"url":"https://github.com/desplega-ai/slack-mock/issues"},"dist":{"shasum":"f50390b1178a18dc0427004a9f09924252cd612a","tarball":"https://registry.npmjs.org/@desplega.ai/slack-mock/-/slack-mock-0.4.0.tgz","fileCount":37,"integrity":"sha512-2wGXzipl94S4+czfLLjyoNmOEwmhE4XgX7QflU+mTqHoJL9c8eKciygeoQLYhfgpfa2DyfCR+mhbmsV8s8khhQ==","signatures":[{"sig":"MEUCIEiuO7SdIYgrW7K3q4gaBfo5x4d5U5weXwglV3BwVgmHAiEAqQsHxBqYB2gC9/t+I8QxIwxXl83pHudCAYAb5DWHD7s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIExwfyt7KSS/Rhq0czh9mlrG/+li/wd5FUuGPYdanVcAAiAQVNyPACpS0bIBzoi4c4C3gpQJ0o4neyIJf+XyVI251g=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@desplega.ai%2fslack-mock@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":470169},"name":"@desplega.ai/slack-mock","type":"module","types":"./dist/index.d.ts","engines":{"bun":">=1.2"},"exports":{".":{"bun":"./src/index.ts","types":"./dist/index.d.ts","default":"./dist/index.js"}},"gitHead":"6397b31a9c5e04a3ba52dd6e82f16ab8b5b10eac","license":"MIT","scripts":{"demo":"bun scripts/demo.ts","lint":"biome check .","test":"bun test","build":"rm -rf dist && bun build src/index.ts src/cli.ts src/screenshot.ts --target=bun --outdir dist --splitting && tsc -p tsconfig.build.json","start":"bun run src/cli.ts serve","release":"npm publish","lint:fix":"biome check --write .","test:e2e":"SLACK_MOCK_E2E=1 bun test test/agent-swarm.e2e.test.ts","typecheck":"tsc --noEmit","prepublishOnly":"bun run typecheck && bun run lint && bun test && bun run build"},"version":"0.4.0","_npmUser":{"name":"tarasyarema","email":"tarasyarema@pm.me"},"homepage":"https://github.com/desplega-ai/slack-mock#readme","keywords":["slack","mock","socket-mode","bolt","testing","e2e"],"repository":{"url":"git+https://github.com/desplega-ai/slack-mock.git","type":"git"},"_npmVersion":"10.9.8","description":"Mock Slack server (Web API + Socket Mode) for end-to-end testing Slack bots","directories":{},"maintainers":[{"name":"tarasyarema","email":"tarasyarema@pm.me"},{"name":"3z3","email":"ezequiel.cura@gmail.com"}],"_nodeVersion":"22.23.2","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"@types/bun":"latest","typescript":"^5","@slack/bolt":"^4.6.0","@biomejs/biome":"^2.4.5"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/slack-mock_0.4.0_1789034929571_0.3302235798416058"}}},"time":{"created":"2026-09-03T11:22:53.177Z","modified":"2026-09-10T10:08:50.226Z","0.1.0":"2026-09-03T11:22:53.537Z","0.1.1":"2026-09-03T21:11:24.538Z","0.1.2":"2026-09-03T22:18:03.275Z","0.2.0":"2026-09-04T10:49:15.884Z","0.3.0":"2026-09-09T17:52:32.593Z","0.4.0":"2026-09-10T10:08:49.672Z"},"bugs":{"url":"https://github.com/desplega-ai/slack-mock/issues"},"license":"MIT","homepage":"https://github.com/desplega-ai/slack-mock#readme","keywords":["slack","mock","socket-mode","bolt","testing","e2e"],"repository":{"url":"git+https://github.com/desplega-ai/slack-mock.git","type":"git"},"description":"Mock Slack server (Web API + Socket Mode) for end-to-end testing Slack bots","maintainers":[{"name":"tarasyarema","email":"tarasyarema@pm.me"},{"name":"3z3","email":"ezequiel.cura@gmail.com"}],"readme":"# slack-mock\n\nA mock Slack server for end-to-end testing Slack bots. It speaks the Slack Web\nAPI and Socket Mode, so a real `@slack/bolt` app connects to it unchanged\n(only `clientOptions.slackApiUrl` differs). Messages, threads, reactions,\nfiles, modals and assistant threads are stored, journaled as JSONL, and\nrendered as Slack-looking HTML for screenshots.\n\nBuilt for the [agent-swarm](../agent-swarm) Slack integration. Bun + TypeScript,\nno runtime dependencies.\n\n## Install\n\n```bash\nbun add -d @desplega.ai/slack-mock          # from npm (once published)\nbun add -d github:desplega-ai/slack-mock    # or straight from the repo (Bun runs the TypeScript sources)\n```\n\n## Quick start (from a checkout)\n\n```bash\nbun install\nbun run start                 # slack-mock serve --port 4040\nbun run demo                  # same, seeded with threads, blocks, files, an ephemeral and a DM\n```\n\nPoint your bot at it:\n\n```bash\nSLACK_BOT_TOKEN=xoxb-mock-bot-token \\\nSLACK_APP_TOKEN=xapp-mock-app-token \\\nSLACK_API_URL=http://127.0.0.1:4040/api/ \\\nbun src/http.ts               # agent-swarm reads SLACK_API_URL into clientOptions.slackApiUrl\n```\n\nTalk to the bot as a human and watch the thread:\n\n```bash\ncurl -X POST http://127.0.0.1:4040/mock/messages -H 'content-type: application/json' \\\n  -d '{\"channel\":\"general\",\"user\":\"alice\",\"text\":\"<@U0BOT00000> hello\"}'\nopen http://127.0.0.1:4040/c/general            # live view: new messages arrive over SSE and flash (?live=0 turns it off, ?refresh=2 polls instead)\nbun src/cli.ts screenshot http://127.0.0.1:4040/c/general --out general.png\n```\n\n## UI\n\nUse the avatar and name chip to choose who posts from every composer.\nThe browser remembers your choice across channels and threads.\nOpen `/c/support?as=taras` to select Taras. The page removes `as` from the URL after selection.\nPublic workspaces show the chip after presenter sign-in.\nOptional user status text appears beside names in the users table and the chip's user list.\nEach message offers reaction and thread icons, including messages without replies.\nThe thread icon opens the thread without focusing the composer.\nIcons stay visible on phones and touch screens. Desktop shows them on hover or keyboard focus.\nChoose an emoji from the picker beside the message, or select an existing reaction to add or remove your own.\nReactions use the identity in the chip and require presenter sign-in in public workspaces.\nBelow 700px, the sidebar becomes a top bar with a channel switcher.\nThreads fill the phone width. The composer follows the visible viewport and includes padding for the iOS home bar.\nCapture the phone layout with `bun test test/phone-ui.test.ts` (requires Chrome).\nThe PNG is saved to `test/artifacts/phone-support-thread.png` at 390 x 844.\n\n## In tests\n\n```ts\nimport { App } from \"@slack/bolt\";\nimport { SlackMock } from \"@desplega.ai/slack-mock\";\n\nconst slack = await SlackMock.start({ port: 0, manifest: \"slack-manifest.json\" });\nconst app = new App({\n  token: slack.env.SLACK_BOT_TOKEN,\n  appToken: slack.env.SLACK_APP_TOKEN,\n  socketMode: true,\n  clientOptions: { slackApiUrl: slack.env.SLACK_API_URL },\n});\nawait app.start();\nawait slack.waitForConnection();\n\nconst ask = await slack.postMessage({ channel: \"general\", user: \"alice\", text: `<@${slack.bot.userId}> hi` });\nconst reply = await slack.waitForMessage({ channel: \"general\", thread_ts: ask.ts, from: \"bot\" });\n```\n\nWhat tests can do:\n\n| humans do | `postMessage` (text, thread, files), `editMessage`, `deleteMessage`, `addReaction`, `removeReaction`, `slashCommand`, `clickButton`, `submitView`, `startAssistantThread`, `changeAssistantContext`, `addUser`, `addChannel`, `openDm`, `invite` |\n|---|---|\n| observe | `messages`, `thread`, `ephemeralMessages`, `findMessages`, `waitForMessage`, `apiCalls`, `waitForApiCall`, `deliveries` (every envelope and its ack), `assistantThread` |\n| break things | `injectFault({ method, error, httpStatus, retryAfterSec, extra })`, `disconnectSockets(\"refresh_requested\")`, options `ackTimeoutMs`, `maxRetries`, `triggerIdTtlMs`, `echoBotMessages`, `subscribedEvents` |\n\nThe same operations exist over HTTP under `/mock/*` for use from another\nprocess (see `docs/design.md`).\n\n## Frames from a journal\n\nA run recorded with `dataFile` can be replayed into one PNG per event that touched a\nthread (or, without a thread, a channel): the view after each `message.add`,\n`message.update`, `message.delete`, `reaction.add` and `reaction.remove` line, in journal\norder, plus `final-thread.png` (a copy of the last frame) and `final-desktop.png` (the full\nUI with the thread panel, 1280x900). Nothing is served: the journal is replayed in memory and\neach page is screenshotted from a temp file by the same headless Chrome helper as\n`screenshot`. A `file.add` line carries no message, so a shared file first appears in the\nframe of the message that shares it.\n\n```bash\nslack-mock frames --journal run.jsonl --channel C0GENERAL0 --thread 1788518410.672000 --out ./frames\n#  5\tmessage.add\t/abs/frames/01-message.add.png\n#  6\treaction.add\t/abs/frames/02-reaction.add.png\n#  ...\n#  final-thread=/abs/frames/final-thread.png\n#  final-desktop=/abs/frames/final-desktop.png\n```\n\nLeave out `--thread` for the channel view. `--manifest app.json` (bot display name),\n`--width`, `--height` and `--no-desktop` are optional. The same from code:\n\n```ts\nimport { frames } from \"@desplega.ai/slack-mock\";\n\nconst result = await frames({ journal: \"run.jsonl\", channel: \"general\", thread: ask.ts, out: \"./frames\" });\n// result.frames[1] = { index: 6, kind: \"reaction.add\", path: \"/abs/frames/02-reaction.add.png\" }\n// result.finalThread, result.finalDesktop\n```\n\nStitch the PNGs into a GIF with ffmpeg if you want motion; the mock only produces frames.\n\n## Slack behaviour that is modelled\n\n- Socket Mode: `apps.connections.open`, `hello`, envelopes for `events_api`,\n  `slash_commands` and `interactive`, acks with response payloads,\n  redelivery with `retry_attempt` when an envelope is not acked, server\n  pings, `disconnect` messages, reconnects.\n- Events: `message` (channels, groups, im), `app_mention`, `message_changed`,\n  `message_deleted`, `file_share`, `reaction_added/removed`,\n  `member_joined_channel`, `assistant_thread_started`,\n  `assistant_thread_context_changed`. The bot's own messages are echoed back\n  like Slack does (Bolt's `ignoreSelf` drops them).\n- Web API: auth, chat (post, update, delete, ephemeral, permalink, streams),\n  conversations (list, info, create, join, invite, archive, history, replies,\n  members, open), users (info, list, lookupByEmail), reactions, files\n  (info, upload v2 flow, download with bearer token), views (open, update,\n  push), assistant.threads (status, title, prompts), pins, bots, team.\n  Errors come back as `{ ok: false, error }` with the real codes\n  (`not_in_channel`, `already_reacted`, `message_not_found`, `name_taken`,\n  `expired_trigger_id`, `message_not_in_streaming_state`, ...).\n- Slash command and interactive `response_url`s are served by the mock\n  (`ephemeral` / `in_channel`, `replace_original`, `delete_original`).\n\n## Commands\n\n```bash\nbun test                      # unit and integration tests (real Bolt client)\nbun run test:e2e              # boots ../agent-swarm against the mock (AGENT_SWARM_REPO to override; needs agent-swarm PR #1310)\nbun run typecheck\nbun run lint\nbun run build                 # dist/ (JS for Bun + .d.ts). CI publishes to npm when the version in package.json changes on main\n```\n\n## Hosted demo\n\nhttps://demo.slack-mock.dev runs `scripts/demo-server.ts`: the mock plus a small\ndemo bot (`scripts/demo-bot.ts`) in one container, deployed to Fly.io from\n`main` by `.github/workflows/deploy.yml` (`fly.toml`, `Dockerfile`). State\nlives in a JSONL journal on a volume and resets daily. Run it yourself:\n\n```bash\ndocker build -t slack-mock-demo . && docker run -p 8080:8080 -e ADMIN_AUTH=demo:secret slack-mock-demo\n```\n\n`PUBLIC_URL`, `ADMIN_AUTH` (basic auth for the UI and `/mock/*`), `DATA_FILE`,\n`RESET_EVERY_HOURS` and `MANIFEST` configure it. The same knobs exist on the\nCLI (`--public-url`, `--auth`) and as `SlackMockOptions` (`publicUrl`, `adminAuth`).\n\nThe same image also serves a workspace for an *external* bot, the Agent Swarm demo at\nhttps://swarm-demo.slack-mock.dev (`fly.swarm.toml`, app `slack-mock-swarm`). Knobs that make\nthat possible, all read by `scripts/demo-server.ts`:\n\n| Env | Effect |\n|---|---|\n| `DEMO_BOT=off` | Skip the built-in demo bot; some other Bolt app connects over Socket Mode. |\n| `SLACK_MOCK_BOT_TOKEN`, `SLACK_MOCK_APP_TOKEN` | The tokens the app must present (`botToken` / `appToken` options). The defaults are public, so set these on any shared host. |\n| `APP_NAME` | Bot display name (`appName`). |\n| `SEED_FILE` | JSON with `users`, `channels`, `messages` and `driver.prompts` (`scripts/seed.ts`); replaces the built-in `#general` and is applied whenever the journal has no channels, so the workspace comes back after every reset. `seeds/agent-swarm-demo.json` is the shipped example. |\n| `UI_PUBLIC=true` | Keep `/` and `/c/...` readable without credentials while `ADMIN_AUTH` still gates `/mock/*` (`publicUi` option). The composer then shows a sign-in instead of the browser's basic-auth prompt. |\n| `PRESENTER_AUTH` | `user:password` that permits `POST /mock/messages`, `POST /mock/reactions`, and `GET /mock/presenter` (`presenterAuth` option). A presenter can post messages and manage reactions without the admin credential. Opening `/c/<channel>#presenter=user:password` signs that browser in. |\n| `DRIVER_INTERVAL_MINUTES` | Every N minutes post the next `driver.prompts` entry as that user, mentioning the bot, while an app is connected (`scripts/driver.ts`). `0` disables it. |\n\n## Docs\n\n- `skills/slack-mock/SKILL.md`: how to use the package from another repo (agents and humans).\n- `docs/design.md`: architecture, decisions, status.\n- `docs/research/`: the research behind it (agent-swarm's inbound/outbound\n  Slack surface, Bolt and socket-mode internals, payload shapes, protocol\n  docs and prior art, e2e conventions, Block Kit surface).\n","readmeFilename":"README.md"}