remote-opencode 1.0.4 → 1.0.7

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
@@ -42,12 +42,6 @@ The bot runs on your development machine alongside OpenCode. When you send a com
42
42
  - [Installation](#installation)
43
43
  - [Quick Start](#quick-start)
44
44
  - [Discord Bot Setup](#discord-bot-setup)
45
- - [Step 1: Create Discord Application](#step-1-create-discord-application)
46
- - [Step 2: Create Bot & Get Token](#step-2-create-bot--get-token)
47
- - [Step 3: Enable Required Intents](#step-3-enable-required-intents)
48
- - [Step 4: Configure Bot Permissions](#step-4-configure-bot-permissions)
49
- - [Step 5: Get Your Server (Guild) ID](#step-5-get-your-server-guild-id)
50
- - [Step 6: Invite Bot to Your Server](#step-6-invite-bot-to-your-server)
51
45
  - [CLI Commands](#cli-commands)
52
46
  - [Discord Slash Commands](#discord-slash-commands)
53
47
  - [Usage Workflow](#usage-workflow)
@@ -104,95 +98,30 @@ That's it! Now use Discord slash commands to interact with OpenCode.
104
98
 
105
99
  ## Discord Bot Setup
106
100
 
107
- Before using remote-opencode, you need to create a Discord Application and Bot. The setup wizard will guide you, but here's a visual walkthrough:
101
+ The setup wizard (`remote-opencode setup`) guides you through the entire process interactively:
108
102
 
109
- ### Step 1: Create Discord Application
103
+ 1. **Opens Discord Developer Portal** in your browser
104
+ 2. **Walks you through** creating an application, enabling intents, and getting your bot token
105
+ 3. **Generates the invite link** automatically and opens it in your browser
106
+ 4. **Deploys slash commands** to your server
110
107
 
111
- 1. Go to [Discord Developer Portal](https://discord.com/developers/applications)
112
- 2. Click **"New Application"**
113
- 3. Enter a name (e.g., "Remote OpenCode")
114
- 4. Copy the **Application ID** — you'll need this later
108
+ Just run `remote-opencode setup` and follow the prompts — no manual URL copying needed!
115
109
 
116
- <img width="800" alt="image" src="https://github.com/user-attachments/assets/ca2e7ff3-91e7-4d66-93dc-c166189c0107" />
110
+ <details>
111
+ <summary>📖 Manual setup reference (click to expand)</summary>
117
112
 
118
- ### Step 2: Create Bot & Get Token
113
+ If you prefer manual setup or need to troubleshoot:
119
114
 
120
- 1. Navigate to the **"Bot"** section in the sidebar
121
- 2. Click **"Reset Token"** (or "View Token" if available)
122
- 3. **Copy the token immediately** it's only shown once!
123
- 4. Keep this token secret never share it publicly
124
-
125
-
126
- ### Step 3: Enable Required Intents
127
-
128
- Still in the **"Bot"** section, scroll down to **"Privileged Gateway Intents"** and enable:
129
-
130
- - ✅ **SERVER MEMBERS INTENT**
131
- - ✅ **MESSAGE CONTENT INTENT**
132
-
133
- Click **"Save Changes"**
134
-
135
- <img width="1500" alt="image" src="https://github.com/user-attachments/assets/d20406ff-26ad-4204-9771-b157c340846a" />
136
-
137
- ### Step 4: Configure Bot Permissions
138
-
139
- The bot needs specific permissions to function properly. You can configure permissions in two ways:
140
-
141
- #### Option A: Using OAuth2 URL Generator (Recommended)
142
-
143
- 1. Navigate to the **"OAuth2"** section in the sidebar
144
- 2. Click on **"URL Generator"**
145
- 3. In **"Scopes"**, select:
146
- - ✅ `bot`
147
- - ✅ `applications.commands`
148
- 4. In **"Bot Permissions"**, select only these essential permissions:
149
-
150
- **General Permissions:**
151
- - ✅ **View Channels** — Required to access channels
152
-
153
- **Text Permissions:**
154
- - ✅ **Send Messages** — Send responses to channels
155
- - ✅ **Create Public Threads** — Create threads for each `/opencode` session
156
- - ✅ **Send Messages in Threads** — Reply within threads
157
- - ✅ **Embed Links** — Send formatted embed messages
158
- - ✅ **Read Message History** — Access context for conversations
159
- - ✅ **Add Reactions** — Add buttons (uses emoji reactions internally)
160
- - ✅ **Use Slash Commands** — Register and use slash commands
161
-
162
- 5. Copy the generated URL at the bottom — this is your bot invite link!
163
-
164
-
165
- #### Option B: Manual Permission Calculation
166
-
167
- If you're building the URL manually, use this permission value:
168
-
169
- ```
170
- https://discord.com/api/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=311385214016&integration_type=0&scope=bot+applications.commands
171
- ```
172
-
173
- **Important:** The URL must include `applications.commands` scope for slash commands to work!
174
-
175
- ### Step 5: Get Your Server (Guild) ID
176
-
177
- 1. Open Discord and go to **User Settings → Advanced**
178
- 2. Enable **"Developer Mode"**
179
- 3. Right-click on your server name in the sidebar
180
- 4. Click **"Copy Server ID"**
181
-
182
- <img width="184" height="530" alt="스크린샷 2026-02-03 오전 2 34 31" src="https://github.com/user-attachments/assets/8ecc2a28-05e5-494f-834f-95d9d0e4e730" />
183
-
184
- ### Step 6: Invite Bot to Your Server
185
-
186
- Use the URL generated in Step 4 (OAuth2 URL Generator), or construct it manually:
187
-
188
- ```
189
- https://discord.com/api/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=218900185540&scope=bot%20applications.commands
190
- ```
191
-
192
- 1. Replace `YOUR_CLIENT_ID` with your Application ID
193
- 2. Open the URL in your browser
194
- 3. Select your server and authorize
115
+ 1. **Create Application**: Go to [Discord Developer Portal](https://discord.com/developers/applications), create a new application
116
+ 2. **Enable Intents**: In "Bot" section, enable SERVER MEMBERS INTENT and MESSAGE CONTENT INTENT
117
+ 3. **Get Bot Token**: In "Bot" section, reset/view token and copy it
118
+ 4. **Get Guild ID**: Enable Developer Mode in Discord settings, right-click your server → Copy Server ID
119
+ 5. **Invite Bot**: Use this URL format:
120
+ ```
121
+ https://discord.com/api/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=2147534848&scope=bot+applications.commands
122
+ ```
195
123
 
124
+ </details>
196
125
 
197
126
  ---
198
127
 
package/dist/src/cli.js CHANGED
@@ -2,12 +2,14 @@
2
2
  import { Command } from 'commander';
3
3
  import pc from 'picocolors';
4
4
  import { createRequire } from 'module';
5
+ import updateNotifier from 'update-notifier';
5
6
  import { runSetupWizard } from './setup/wizard.js';
6
7
  import { deployCommands } from './setup/deploy.js';
7
8
  import { startBot } from './bot.js';
8
9
  import { hasBotConfig, getConfigDir } from './services/configStore.js';
9
10
  const require = createRequire(import.meta.url);
10
11
  const pkg = require('../../package.json');
12
+ updateNotifier({ pkg }).notify({ isGlobal: true });
11
13
  const program = new Command();
12
14
  program
13
15
  .name('remote-opencode')
@@ -1,8 +1,11 @@
1
1
  import * as p from '@clack/prompts';
2
2
  import pc from 'picocolors';
3
+ import open from 'open';
3
4
  import { setBotConfig, getBotConfig, hasBotConfig } from '../services/configStore.js';
4
5
  import { deployCommands } from './deploy.js';
5
6
  const DISCORD_DEV_URL = 'https://discord.com/developers/applications';
7
+ const BOT_PERMISSIONS = '2147534848';
8
+ const BOT_SCOPES = 'bot applications.commands';
6
9
  function validateApplicationId(value) {
7
10
  if (!value)
8
11
  return 'Application ID is required';
@@ -24,6 +27,21 @@ function validateGuildId(value) {
24
27
  return 'Invalid format (should be 17-20 digits)';
25
28
  return undefined;
26
29
  }
30
+ function generateInviteUrl(clientId) {
31
+ const url = new URL('https://discord.com/api/oauth2/authorize');
32
+ url.searchParams.set('client_id', clientId);
33
+ url.searchParams.set('permissions', BOT_PERMISSIONS);
34
+ url.searchParams.set('scope', BOT_SCOPES);
35
+ return url.toString();
36
+ }
37
+ async function openUrl(url) {
38
+ try {
39
+ await open(url);
40
+ }
41
+ catch {
42
+ // Silently fail - URL is displayed to user anyway
43
+ }
44
+ }
27
45
  export async function runSetupWizard() {
28
46
  console.clear();
29
47
  p.intro(pc.bgCyan(pc.black(' remote-opencode setup ')));
@@ -38,10 +56,21 @@ export async function runSetupWizard() {
38
56
  return;
39
57
  }
40
58
  }
41
- p.note(`1. Go to ${pc.cyan(DISCORD_DEV_URL)}\n` +
42
- `2. Click ${pc.bold('"New Application"')}\n` +
43
- `3. Give your application a name\n` +
44
- `4. Copy the ${pc.bold('Application ID')} from "General Information"`, 'Step 1: Create Discord Application');
59
+ // Step 1: Open Discord Developer Portal
60
+ p.note(`We'll open the Discord Developer Portal in your browser.\n\n` +
61
+ `1. Click ${pc.bold('"New Application"')}\n` +
62
+ `2. Give your application a name (e.g., "Remote OpenCode")\n` +
63
+ `3. Copy the ${pc.bold('Application ID')} from "General Information"`, 'Step 1: Create Discord Application');
64
+ const openPortal = await p.text({
65
+ message: `Press ${pc.cyan('Enter')} to open Discord Developer Portal...`,
66
+ placeholder: 'Press Enter',
67
+ defaultValue: '',
68
+ });
69
+ if (p.isCancel(openPortal)) {
70
+ p.cancel('Setup cancelled.');
71
+ process.exit(0);
72
+ }
73
+ await openUrl(DISCORD_DEV_URL);
45
74
  const clientId = await p.text({
46
75
  message: 'Enter your Discord Application ID:',
47
76
  placeholder: 'e.g., 1234567890123456789',
@@ -51,19 +80,27 @@ export async function runSetupWizard() {
51
80
  p.cancel('Setup cancelled.');
52
81
  process.exit(0);
53
82
  }
54
- p.note(`1. Go to the ${pc.bold('"Bot"')} section in the left sidebar\n` +
83
+ // Step 2: Enable Intents (guidance only)
84
+ p.note(`In the Discord Developer Portal:\n\n` +
85
+ `1. Go to the ${pc.bold('"Bot"')} section in the left sidebar\n` +
55
86
  `2. Scroll down to ${pc.bold('"Privileged Gateway Intents"')}\n` +
56
87
  `3. Enable these intents:\n` +
57
- ` ${pc.green('SERVER MEMBERS INTENT')}\n` +
58
- ` ${pc.green('MESSAGE CONTENT INTENT')}\n` +
59
- `4. Click "Save Changes"`, 'Step 2: Enable Required Intents');
60
- await p.confirm({
88
+ ` ${pc.green('*')} SERVER MEMBERS INTENT\n` +
89
+ ` ${pc.green('*')} MESSAGE CONTENT INTENT\n` +
90
+ `4. Click ${pc.bold('"Save Changes"')}`, 'Step 2: Enable Required Intents');
91
+ const intentsConfirm = await p.confirm({
61
92
  message: 'Have you enabled the required intents?',
62
93
  initialValue: true,
63
94
  });
64
- p.note(`1. In the ${pc.bold('"Bot"')} section\n` +
95
+ if (p.isCancel(intentsConfirm)) {
96
+ p.cancel('Setup cancelled.');
97
+ process.exit(0);
98
+ }
99
+ // Step 3: Get Bot Token (guidance only)
100
+ p.note(`Still in the Discord Developer Portal:\n\n` +
101
+ `1. In the ${pc.bold('"Bot"')} section\n` +
65
102
  `2. Click ${pc.bold('"Reset Token"')} (or "View Token" if available)\n` +
66
- `3. Copy the token (it's only shown once!)`, 'Step 3: Get Bot Token');
103
+ `3. Copy the token ${pc.dim('(it\'s only shown once!)')}`, 'Step 3: Get Bot Token');
67
104
  const discordToken = await p.password({
68
105
  message: 'Enter your Discord Bot Token:',
69
106
  validate: validateToken,
@@ -72,7 +109,8 @@ export async function runSetupWizard() {
72
109
  p.cancel('Setup cancelled.');
73
110
  process.exit(0);
74
111
  }
75
- p.note(`1. Open Discord and go to User Settings > Advanced\n` +
112
+ // Step 4: Get Guild ID (guidance only)
113
+ p.note(`1. Open Discord and go to ${pc.bold('User Settings > Advanced')}\n` +
76
114
  `2. Enable ${pc.bold('"Developer Mode"')}\n` +
77
115
  `3. Right-click on your server name\n` +
78
116
  `4. Click ${pc.bold('"Copy Server ID"')}`, 'Step 4: Get Guild (Server) ID');
@@ -85,6 +123,7 @@ export async function runSetupWizard() {
85
123
  p.cancel('Setup cancelled.');
86
124
  process.exit(0);
87
125
  }
126
+ // Save configuration
88
127
  const s = p.spinner();
89
128
  s.start('Saving configuration...');
90
129
  setBotConfig({
@@ -93,9 +132,22 @@ export async function runSetupWizard() {
93
132
  guildId: guildId,
94
133
  });
95
134
  s.stop('Configuration saved!');
96
- const inviteUrl = `https://discord.com/api/oauth2/authorize?client_id=${clientId}&permissions=2147534848&scope=bot`;
97
- p.note(`Open this URL in your browser:\n\n${pc.cyan(inviteUrl)}\n\n` +
98
- `Select your server and authorize the bot.`, 'Step 5: Invite Bot to Server');
135
+ // Step 5: Invite Bot to Server
136
+ const inviteUrl = generateInviteUrl(clientId);
137
+ p.note(`We'll open the bot invite page in your browser.\n\n` +
138
+ `1. Select your server\n` +
139
+ `2. Click ${pc.bold('"Authorize"')}\n\n` +
140
+ `${pc.dim('URL: ' + inviteUrl)}`, 'Step 5: Invite Bot to Server');
141
+ const openInvite = await p.text({
142
+ message: `Press ${pc.cyan('Enter')} to open the invite page...`,
143
+ placeholder: 'Press Enter',
144
+ defaultValue: '',
145
+ });
146
+ if (p.isCancel(openInvite)) {
147
+ p.cancel('Setup cancelled.');
148
+ process.exit(0);
149
+ }
150
+ await openUrl(inviteUrl);
99
151
  const invited = await p.confirm({
100
152
  message: 'Have you invited the bot to your server?',
101
153
  initialValue: true,
@@ -104,6 +156,7 @@ export async function runSetupWizard() {
104
156
  p.cancel('Setup cancelled.');
105
157
  process.exit(0);
106
158
  }
159
+ // Step 6: Deploy Commands
107
160
  const shouldDeploy = await p.confirm({
108
161
  message: 'Deploy slash commands now?',
109
162
  initialValue: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-opencode",
3
- "version": "1.0.4",
3
+ "version": "1.0.7",
4
4
  "description": "Discord bot for remote OpenCode CLI access",
5
5
  "main": "dist/src/index.js",
6
6
  "bin": {
@@ -41,10 +41,13 @@
41
41
  "discord.js": "^14.25.1",
42
42
  "eventsource": "^4.1.0",
43
43
  "node-pty": "^1.1.0",
44
- "picocolors": "^1.1.1"
44
+ "open": "^10.1.0",
45
+ "picocolors": "^1.1.1",
46
+ "update-notifier": "^7.3.1"
45
47
  },
46
48
  "devDependencies": {
47
49
  "@types/node": "^25.1.0",
50
+ "@types/update-notifier": "^6.0.8",
48
51
  "ts-node": "^10.9.2",
49
52
  "typescript": "^5.9.3",
50
53
  "vitest": "^4.0.18"