astro-claw 1.0.0
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/.env.example +12 -0
- package/LICENSE +21 -0
- package/README.md +59 -0
- package/astronaut-icon.png +0 -0
- package/index.js +833 -0
- package/mcp-servers.example.json +16 -0
- package/package.json +56 -0
- package/setup.js +316 -0
- package/slack-manifest.json +47 -0
- package/slack-manifest.yml +34 -0
- package/slack-setup.js +499 -0
- package/start.js +64 -0
- package/workspace/CLAUDE.md +58 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"apify": {
|
|
3
|
+
"command": "npx",
|
|
4
|
+
"args": ["-y", "@apify/actors-mcp-server"],
|
|
5
|
+
"env": {
|
|
6
|
+
"APIFY_TOKEN": "your-apify-token-here"
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
"airtable": {
|
|
10
|
+
"command": "npx",
|
|
11
|
+
"args": ["-y", "airtable-mcp-server"],
|
|
12
|
+
"env": {
|
|
13
|
+
"AIRTABLE_API_KEY": "your-airtable-pat-here"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "astro-claw",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Claude Code over Slack — your AI crew member. One command: npx astro-claw",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"astro-claw": "./start.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node start.js",
|
|
12
|
+
"dev": "nodemon --watch index.js --watch start.js --watch setup.js start.js",
|
|
13
|
+
"setup": "node start.js --setup"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"slack",
|
|
17
|
+
"claude",
|
|
18
|
+
"ai",
|
|
19
|
+
"agent",
|
|
20
|
+
"claude-code",
|
|
21
|
+
"bot",
|
|
22
|
+
"astronaut"
|
|
23
|
+
],
|
|
24
|
+
"author": "Taxflow <felipe@jointaxflow.ai>",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/felipesalinasr/astro-claw"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18.0.0"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"start.js",
|
|
35
|
+
"setup.js",
|
|
36
|
+
"slack-setup.js",
|
|
37
|
+
"index.js",
|
|
38
|
+
"slack-manifest.json",
|
|
39
|
+
"slack-manifest.yml",
|
|
40
|
+
"astronaut-icon.png",
|
|
41
|
+
"workspace/CLAUDE.md",
|
|
42
|
+
"mcp-servers.example.json",
|
|
43
|
+
".env.example"
|
|
44
|
+
],
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.85",
|
|
47
|
+
"@slack/bolt": "^4.1.0",
|
|
48
|
+
"dotenv": "^16.4.0"
|
|
49
|
+
},
|
|
50
|
+
"optionalDependencies": {
|
|
51
|
+
"puppeteer-core": "^24.40.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"nodemon": "^3.1.0"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/setup.js
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { createInterface } from "node:readline/promises";
|
|
2
|
+
import { execSync, spawnSync } from "node:child_process";
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { resolve, dirname } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
// Config paths — use ASTRO_CLAW_HOME (set by start.js) or fall back to __dirname
|
|
10
|
+
const CONFIG_HOME = process.env.ASTRO_CLAW_HOME || __dirname;
|
|
11
|
+
const ENV_PATH = resolve(CONFIG_HOME, ".env");
|
|
12
|
+
const MCP_PATH = resolve(CONFIG_HOME, "mcp-servers.json");
|
|
13
|
+
const MANIFEST_PATH = resolve(__dirname, "slack-manifest.json"); // always from package
|
|
14
|
+
|
|
15
|
+
// ─── Terminal Colors ────────────────────────────────────────────────
|
|
16
|
+
const bold = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
17
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
18
|
+
const red = (s) => `\x1b[31m${s}\x1b[0m`;
|
|
19
|
+
const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
|
|
20
|
+
const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
|
|
21
|
+
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
22
|
+
|
|
23
|
+
const CHECK = green("✓");
|
|
24
|
+
const CROSS = red("✗");
|
|
25
|
+
const WARN = yellow("!");
|
|
26
|
+
|
|
27
|
+
function step(num, total, label) {
|
|
28
|
+
console.log(`\n ${bold(`Step ${num}/${total}:`)} ${label}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ─── Step 1: Node Version ───────────────────────────────────────────
|
|
32
|
+
function checkNodeVersion() {
|
|
33
|
+
const major = parseInt(process.versions.node.split(".")[0], 10);
|
|
34
|
+
if (major < 18) {
|
|
35
|
+
console.log(` ${CROSS} Node.js ${process.versions.node} — need v18 or higher`);
|
|
36
|
+
console.log(` Download: https://nodejs.org/`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
console.log(` ${CHECK} Node.js v${process.versions.node}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ─── Step 2: Claude Code CLI ────────────────────────────────────────
|
|
43
|
+
async function checkClaudeCli(rl) {
|
|
44
|
+
let version;
|
|
45
|
+
try {
|
|
46
|
+
version = execSync("claude --version", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
47
|
+
} catch {
|
|
48
|
+
version = null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (version) {
|
|
52
|
+
console.log(` ${CHECK} Claude Code CLI ${version}`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log(` ${WARN} Claude Code CLI not found`);
|
|
57
|
+
const answer = await rl.question(` Install it now? ${dim("[Y/n]")} `);
|
|
58
|
+
|
|
59
|
+
if (answer.toLowerCase() === "n") {
|
|
60
|
+
console.log(`\n Install manually: ${cyan("npm install -g @anthropic-ai/claude-code")}`);
|
|
61
|
+
console.log(` Then run ${cyan("npx astro-claw")} again.\n`);
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log(` Installing Claude Code CLI...`);
|
|
66
|
+
try {
|
|
67
|
+
execSync("npm install -g @anthropic-ai/claude-code", { stdio: "inherit" });
|
|
68
|
+
const v = execSync("claude --version", { encoding: "utf-8", stdio: "pipe" }).trim();
|
|
69
|
+
console.log(` ${CHECK} Claude Code CLI ${v}`);
|
|
70
|
+
} catch {
|
|
71
|
+
console.log(` ${CROSS} Installation failed.`);
|
|
72
|
+
console.log(` Try: ${cyan("sudo npm install -g @anthropic-ai/claude-code")}`);
|
|
73
|
+
console.log(` Then run ${cyan("npx astro-claw")} again.\n`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ─── Step 3: Claude Authentication ──────────────────────────────────
|
|
79
|
+
async function checkClaudeAuth(rl) {
|
|
80
|
+
function isLoggedIn() {
|
|
81
|
+
try {
|
|
82
|
+
const out = execSync("claude auth status", { encoding: "utf-8", stdio: "pipe" });
|
|
83
|
+
const data = JSON.parse(out);
|
|
84
|
+
return data.loggedIn ? data : null;
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const auth = isLoggedIn();
|
|
91
|
+
if (auth) {
|
|
92
|
+
const who = auth.email || auth.authMethod || "authenticated";
|
|
93
|
+
console.log(` ${CHECK} Claude authenticated (${who})`);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log(` ${WARN} Not logged in to Claude`);
|
|
98
|
+
console.log(` A browser window will open for you to log in.`);
|
|
99
|
+
await rl.question(` Press Enter to continue... `);
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
spawnSync("claude", ["auth", "login"], { stdio: "inherit" });
|
|
103
|
+
} catch {
|
|
104
|
+
// check status next
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (isLoggedIn()) {
|
|
108
|
+
console.log(` ${CHECK} Claude authenticated`);
|
|
109
|
+
} else {
|
|
110
|
+
console.log(` ${CROSS} Authentication failed.`);
|
|
111
|
+
console.log(` Run ${cyan("claude auth login")} manually, then ${cyan("npx astro-claw")} again.\n`);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ─── Step 4: Slack App Setup ────────────────────────────────────────
|
|
117
|
+
// Returns { botToken, appToken, signingSecret } — some may be null
|
|
118
|
+
async function setupSlackApp(rl) {
|
|
119
|
+
// Try self-driving Chrome first
|
|
120
|
+
let autoTokens = null;
|
|
121
|
+
try {
|
|
122
|
+
const { findChrome } = await import("./slack-setup.js");
|
|
123
|
+
if (findChrome()) {
|
|
124
|
+
console.log(`\n Chrome detected — launching self-driving setup...`);
|
|
125
|
+
const { default: selfDrivingSlackSetup } = await import("./slack-setup.js");
|
|
126
|
+
autoTokens = await selfDrivingSlackSetup();
|
|
127
|
+
}
|
|
128
|
+
} catch (err) {
|
|
129
|
+
console.log(` ${WARN} Auto-setup unavailable: ${err.message}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// If auto-setup got all tokens, we're done
|
|
133
|
+
if (autoTokens?.botToken && autoTokens?.appToken && autoTokens?.signingSecret) {
|
|
134
|
+
return autoTokens;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Fall back to manual flow for any missing tokens
|
|
138
|
+
if (!autoTokens) {
|
|
139
|
+
// Full manual flow — open browser with manifest
|
|
140
|
+
let manifest;
|
|
141
|
+
try { manifest = readFileSync(MANIFEST_PATH, "utf-8"); } catch { manifest = null; }
|
|
142
|
+
|
|
143
|
+
if (manifest) {
|
|
144
|
+
const slackUrl = `https://api.slack.com/apps?new_app=1&manifest_json=${encodeURIComponent(manifest)}`;
|
|
145
|
+
console.log(`\n Opening your browser with the app manifest pre-filled...\n`);
|
|
146
|
+
try {
|
|
147
|
+
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
148
|
+
execSync(`${openCmd} "${slackUrl}"`, { stdio: "ignore" });
|
|
149
|
+
console.log(` ${CHECK} Browser opened`);
|
|
150
|
+
} catch {
|
|
151
|
+
console.log(` ${WARN} Couldn't open browser. Go to: ${cyan(slackUrl)}`);
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
console.log(`\n Go to: ${cyan("https://api.slack.com/apps?new_app=1")}`);
|
|
155
|
+
console.log(` Choose "From a manifest" → paste the JSON from slack-manifest.json`);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
console.log(`\n ${bold("1.")} Select your workspace → click ${bold("Next")} → ${bold("Create")}`);
|
|
159
|
+
console.log(` ${bold("2.")} Go to ${bold("Basic Information")} → ${bold("App-Level Tokens")} → ${bold("Generate Token")}`);
|
|
160
|
+
console.log(` Name it anything → add scope ${bold("connections:write")} → ${bold("Generate")}`);
|
|
161
|
+
console.log(` ${bold("3.")} Go to ${bold("OAuth & Permissions")} → ${bold("Install to Workspace")} → ${bold("Allow")}`);
|
|
162
|
+
console.log(` ${bold("4.")} ${dim("(Optional)")} Set the bot icon: ${bold("Basic Information")} → ${bold("Display Information")}`);
|
|
163
|
+
console.log(` Upload the ${cyan("astronaut-icon.png")} file from this package\n`);
|
|
164
|
+
await rl.question(` Press Enter when done... `);
|
|
165
|
+
} else {
|
|
166
|
+
// Partial auto — tell user which ones were captured
|
|
167
|
+
const missing = [];
|
|
168
|
+
if (!autoTokens.botToken) missing.push("Bot Token");
|
|
169
|
+
if (!autoTokens.appToken) missing.push("App-Level Token");
|
|
170
|
+
if (!autoTokens.signingSecret) missing.push("Signing Secret");
|
|
171
|
+
console.log(` ${WARN} Still need: ${bold(missing.join(", "))}`);
|
|
172
|
+
console.log(` Grab them from your Slack app dashboard.\n`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Collect any missing tokens manually
|
|
176
|
+
return await collectMissingTokens(rl, autoTokens || {});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ─── Step 5: Collect Missing Tokens ─────────────────────────────────
|
|
180
|
+
async function collectMissingTokens(rl, existing) {
|
|
181
|
+
async function askToken(label, hint, validator, errorMsg) {
|
|
182
|
+
while (true) {
|
|
183
|
+
const value = (await rl.question(` ${label} ${dim(hint)} `)).trim();
|
|
184
|
+
if (!value) { console.log(` ${CROSS} Required.`); continue; }
|
|
185
|
+
if (validator(value)) return value;
|
|
186
|
+
console.log(` ${CROSS} ${errorMsg}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const needAny = !existing.botToken || !existing.appToken || !existing.signingSecret;
|
|
191
|
+
if (needAny) {
|
|
192
|
+
console.log(`\n Paste your tokens from the Slack app dashboard:\n`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const botToken = existing.botToken || await askToken("Bot Token:", "(OAuth & Permissions > Bot User OAuth Token)", (v) => v.startsWith("xoxb-"), "Must start with xoxb-");
|
|
196
|
+
const appToken = existing.appToken || await askToken("App-Level Token:", "(Basic Information > App-Level Tokens)", (v) => v.startsWith("xapp-"), "Must start with xapp-");
|
|
197
|
+
const signingSecret = existing.signingSecret || await askToken("Signing Secret:", "(Basic Information > Signing Secret)", (v) => /^[0-9a-f]{20,}$/i.test(v), "Must be a hex string (20+ chars)");
|
|
198
|
+
|
|
199
|
+
return { botToken, appToken, signingSecret };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ─── Step 6: Validate Tokens ────────────────────────────────────────
|
|
203
|
+
async function validateTokens(tokens) {
|
|
204
|
+
console.log(`\n Validating...`);
|
|
205
|
+
try {
|
|
206
|
+
const { WebClient } = await import("@slack/web-api");
|
|
207
|
+
const client = new WebClient(tokens.botToken);
|
|
208
|
+
const result = await client.auth.test();
|
|
209
|
+
console.log(` ${CHECK} Connected to workspace ${bold(`"${result.team}"`)} as ${bold(result.user)}`);
|
|
210
|
+
return true;
|
|
211
|
+
} catch (err) {
|
|
212
|
+
console.log(` ${CROSS} Bot token validation failed: ${err?.data?.error || err.message || "unknown"}`);
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ─── Step 7: Optional Config ────────────────────────────────────────
|
|
218
|
+
async function collectOptionalConfig(rl) {
|
|
219
|
+
console.log(`\n Optional configuration:\n`);
|
|
220
|
+
console.log(` ${dim("Find your Slack user ID: click your profile > ⋯ > Copy member ID")}`);
|
|
221
|
+
const adminIds = (await rl.question(` Admin User ID(s) ${dim("(comma-separated, blank to skip)")} `)).trim();
|
|
222
|
+
|
|
223
|
+
if (adminIds) {
|
|
224
|
+
const valid = adminIds.split(",").map((s) => s.trim()).every((id) => /^[UWB][A-Z0-9]{6,15}$/.test(id));
|
|
225
|
+
if (!valid) console.log(` ${WARN} Some IDs look invalid, saving anyway.`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return { adminIds };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// ─── Step 8: Write .env ─────────────────────────────────────────────
|
|
232
|
+
function writeEnvFile(tokens, optional) {
|
|
233
|
+
const content = `# Astro Claw — generated by setup wizard
|
|
234
|
+
# Config home: ${CONFIG_HOME}
|
|
235
|
+
# Re-run setup: npx astro-claw --setup
|
|
236
|
+
|
|
237
|
+
# Slack tokens
|
|
238
|
+
SLACK_BOT_TOKEN=${tokens.botToken}
|
|
239
|
+
SLACK_APP_TOKEN=${tokens.appToken}
|
|
240
|
+
SLACK_SIGNING_SECRET=${tokens.signingSecret}
|
|
241
|
+
|
|
242
|
+
# No API key needed — uses your Claude CLI login.
|
|
243
|
+
# Verify: claude auth status
|
|
244
|
+
|
|
245
|
+
# Admin user IDs (comma-separated, blank = all users)
|
|
246
|
+
ADMIN_USER_IDS=${optional.adminIds || ""}
|
|
247
|
+
`;
|
|
248
|
+
writeFileSync(ENV_PATH, content);
|
|
249
|
+
console.log(` ${CHECK} Configuration saved to ${ENV_PATH}`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ─── Step 9: MCP Servers ────────────────────────────────────────────
|
|
253
|
+
function setupMcpServers() {
|
|
254
|
+
if (!existsSync(MCP_PATH)) {
|
|
255
|
+
writeFileSync(MCP_PATH, "{}\n");
|
|
256
|
+
console.log(` ${CHECK} MCP servers config created ${dim("(add servers later with !mcp in Slack)")}`);
|
|
257
|
+
} else {
|
|
258
|
+
console.log(` ${CHECK} MCP servers config exists`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ─── Main ───────────────────────────────────────────────────────────
|
|
263
|
+
export default async function runSetup() {
|
|
264
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
265
|
+
|
|
266
|
+
console.log("");
|
|
267
|
+
console.log(bold(" 🚀 Astro Claw Setup"));
|
|
268
|
+
console.log(dim(" ─────────────────────────────────────────"));
|
|
269
|
+
console.log("");
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
step(1, 4, "Checking Node.js");
|
|
273
|
+
checkNodeVersion();
|
|
274
|
+
|
|
275
|
+
step(2, 4, "Claude Code CLI");
|
|
276
|
+
await checkClaudeCli(rl);
|
|
277
|
+
|
|
278
|
+
step(3, 4, "Claude authentication");
|
|
279
|
+
await checkClaudeAuth(rl);
|
|
280
|
+
|
|
281
|
+
step(4, 4, "Slack app setup");
|
|
282
|
+
let tokens = await setupSlackApp(rl);
|
|
283
|
+
|
|
284
|
+
// Validate tokens
|
|
285
|
+
let validated = await validateTokens(tokens);
|
|
286
|
+
while (!validated) {
|
|
287
|
+
const retry = await rl.question(`\n Re-enter tokens? ${dim("[Y/n]")} `);
|
|
288
|
+
if (retry.toLowerCase() === "n") { rl.close(); return false; }
|
|
289
|
+
tokens = await collectMissingTokens(rl, {});
|
|
290
|
+
validated = await validateTokens(tokens);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Optional config + save
|
|
294
|
+
const optional = await collectOptionalConfig(rl);
|
|
295
|
+
writeEnvFile(tokens, optional);
|
|
296
|
+
setupMcpServers();
|
|
297
|
+
|
|
298
|
+
rl.close();
|
|
299
|
+
|
|
300
|
+
console.log("");
|
|
301
|
+
console.log(bold(green(" ✓ Setup complete!")));
|
|
302
|
+
console.log(dim(" ─────────────────────────────────────────"));
|
|
303
|
+
console.log(` Config saved to: ${cyan(CONFIG_HOME)}`);
|
|
304
|
+
console.log(` Starting Astro Claw...\n`);
|
|
305
|
+
|
|
306
|
+
return true;
|
|
307
|
+
} catch (err) {
|
|
308
|
+
rl.close();
|
|
309
|
+
if (err.code === "ERR_USE_AFTER_CLOSE" || err.message?.includes("readline was closed")) {
|
|
310
|
+
console.log(`\n\n Setup cancelled. Run ${cyan("npx astro-claw")} when ready.\n`);
|
|
311
|
+
} else {
|
|
312
|
+
console.error(`\n Setup error: ${err.message}\n`);
|
|
313
|
+
}
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"display_information": {
|
|
3
|
+
"name": "Astronaut",
|
|
4
|
+
"description": "AI crew member powered by Claude Code",
|
|
5
|
+
"background_color": "#1a1a2e"
|
|
6
|
+
},
|
|
7
|
+
"features": {
|
|
8
|
+
"app_home": {
|
|
9
|
+
"home_tab_enabled": false,
|
|
10
|
+
"messages_tab_enabled": true,
|
|
11
|
+
"messages_tab_read_only_enabled": false
|
|
12
|
+
},
|
|
13
|
+
"bot_user": {
|
|
14
|
+
"display_name": "Astronaut",
|
|
15
|
+
"always_online": true
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"oauth_config": {
|
|
19
|
+
"scopes": {
|
|
20
|
+
"bot": [
|
|
21
|
+
"app_mentions:read",
|
|
22
|
+
"channels:history",
|
|
23
|
+
"chat:write",
|
|
24
|
+
"files:read",
|
|
25
|
+
"groups:history",
|
|
26
|
+
"im:history",
|
|
27
|
+
"im:read",
|
|
28
|
+
"im:write",
|
|
29
|
+
"mpim:history"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"settings": {
|
|
34
|
+
"event_subscriptions": {
|
|
35
|
+
"bot_events": [
|
|
36
|
+
"app_mention",
|
|
37
|
+
"message.im"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"interactivity": {
|
|
41
|
+
"is_enabled": false
|
|
42
|
+
},
|
|
43
|
+
"org_deploy_enabled": false,
|
|
44
|
+
"socket_mode_enabled": true,
|
|
45
|
+
"token_rotation_enabled": false
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
display_information:
|
|
2
|
+
name: Astronaut
|
|
3
|
+
description: AI crew member powered by Claude Code
|
|
4
|
+
background_color: "#1a1a2e"
|
|
5
|
+
features:
|
|
6
|
+
app_home:
|
|
7
|
+
home_tab_enabled: false
|
|
8
|
+
messages_tab_enabled: true
|
|
9
|
+
messages_tab_read_only_enabled: false
|
|
10
|
+
bot_user:
|
|
11
|
+
display_name: Astronaut
|
|
12
|
+
always_online: true
|
|
13
|
+
oauth_config:
|
|
14
|
+
scopes:
|
|
15
|
+
bot:
|
|
16
|
+
- app_mentions:read
|
|
17
|
+
- channels:history
|
|
18
|
+
- chat:write
|
|
19
|
+
- files:read
|
|
20
|
+
- groups:history
|
|
21
|
+
- im:history
|
|
22
|
+
- im:read
|
|
23
|
+
- im:write
|
|
24
|
+
- mpim:history
|
|
25
|
+
settings:
|
|
26
|
+
event_subscriptions:
|
|
27
|
+
bot_events:
|
|
28
|
+
- app_mention
|
|
29
|
+
- message.im
|
|
30
|
+
interactivity:
|
|
31
|
+
is_enabled: false
|
|
32
|
+
org_deploy_enabled: false
|
|
33
|
+
socket_mode_enabled: true
|
|
34
|
+
token_rotation_enabled: false
|