imtoagent 0.3.5 โ†’ 0.3.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
@@ -4,12 +4,50 @@ Connect Feishu, Telegram, personal WeChat, and WeCom to AI coding agents like Cl
4
4
 
5
5
  One gateway, multiple IMs, multiple agents, unified port proxy.
6
6
 
7
+ ## ๐Ÿš€ Quick Start (5 Minutes)
8
+
9
+ ### Step 1: Install (One Command)
10
+
11
+ ```bash
12
+ curl -fsSL https://raw.githubusercontent.com/imtoagent/imtoagent/main/install.sh | bash
13
+ ```
14
+
15
+ This script detects your environment, installs bun if needed, installs imtoagent, and guides you through setup.
16
+
17
+ ### Step 2: Start
18
+
19
+ ```bash
20
+ imtoagent setup
21
+ ```
22
+
23
+ The interactive wizard guides you through:
24
+ 1. Select IM platform (Feishu/Telegram/WeChat/WeCom)
25
+ 2. Enter Bot credentials
26
+ 3. Choose Agent backend (Claude Code/Codex/OpenCode)
27
+ 4. Configure model providers (API keys)
28
+ 5. Generate soul files for personality injection
29
+
30
+ ### Step 2: Verify
31
+
32
+ ```bash
33
+ imtoagent status # check it's running
34
+ ```
35
+
36
+ That's it! Send `/help` to your Bot in the IM to see available commands.
37
+
38
+ ---
39
+
40
+ **Alternative install methods:** See [Installation Methods](#installation-methods) below for npm global install or source install.
41
+
42
+ ---
43
+
7
44
  ## Architecture
8
45
 
9
46
  ```
10
- ้ฃžไนฆ/Telegram/ๅพฎไฟก/ไผๅพฎ โ†’ IM Registry ๅทฅๅŽ‚ โ†’ Bot ๅฎžไพ‹
11
- โ†’ AgentRuntime SDK โ†’ Agent Adapter
12
- โ†’ ็ปŸไธ€ Proxy (:18899) โ†’ ไธŠๆธธๆจกๅž‹
47
+ IM Platform (Feishu/Telegram/WeChat/WeCom)
48
+ โ†’ IM Registry Factory โ†’ Bot Instance
49
+ โ†’ AgentRuntime SDK โ†’ Agent Adapter
50
+ โ†’ Unified Proxy (:18899) โ†’ Upstream Models
13
51
  ```
14
52
 
15
53
  ### Supported IM Adapters
@@ -43,63 +81,115 @@ One gateway, multiple IMs, multiple agents, unified port proxy.
43
81
  | Codex | `npm install -g @openai/codex` |
44
82
  | OpenCode | `npm install -g opencode` |
45
83
 
46
- ### Installation
84
+ ## Installation Methods
47
85
 
48
- #### Method 1: npm global install (recommended)
86
+ ### Method 1: One-Click Install (Recommended)
87
+
88
+ ```bash
89
+ curl -fsSL https://raw.githubusercontent.com/imtoagent/imtoagent/main/install.sh | bash
90
+ ```
91
+
92
+ This script does everything automatically:
93
+ - Detects your OS and environment
94
+ - Installs bun if missing
95
+ - Installs or upgrades imtoagent
96
+ - Runs the setup wizard if no configuration exists
97
+ - Starts the gateway and verifies it's running
98
+
99
+ **Flags:**
100
+ - `--non-interactive` โ€” Skip all prompts, auto-install and auto-start
101
+ - `--skip-bun` โ€” Skip bun installation check
102
+ - `--skip-start` โ€” Don't start the gateway after install
103
+
104
+ ### Method 2: npm Global Install
49
105
 
50
106
  ```bash
51
107
  npm install -g imtoagent
52
108
  ```
53
109
 
54
- After installation, it automatically checks whether initial configuration is needed. An interactive terminal will guide you through the setup wizard.
110
+ This is the simplest approach. After installation, the post-install script checks if you need initial configuration.
55
111
 
56
- #### Method 2: Source install
112
+ ### Method 3: Source Install
57
113
 
58
114
  ```bash
59
- git clone https://github.com/YOUR_USERNAME/imtoagent.git
115
+ git clone https://github.com/imtoagent/imtoagent.git
60
116
  cd imtoagent
61
117
  bun install
62
118
  bun run bin/imtoagent setup
63
119
  ```
64
120
 
65
- ### First-Time Configuration
121
+ Use this for development or if you want to modify the source code.
122
+
123
+ ### Prerequisites
124
+
125
+ - **Bun** runtime (โ‰ฅ1.0.0): `brew install oven-sh/bun/bun`
126
+ - **macOS or Linux**
127
+ - **At least one Agent backend** installed (see below)
128
+
129
+ ### Agent Backend Installation
130
+
131
+ | Backend | Install Command |
132
+ |---------|----------------|
133
+ | Claude Code | `npm install -g @anthropic-ai/claude-agent-sdk` |
134
+ | Codex | `npm install -g @openai/codex` |
135
+ | OpenCode | `npm install -g opencode` |
136
+
137
+ You can install backends before or after installing imtoagent.
138
+
139
+ ## Configuration
140
+
141
+ ### First-Time Setup
66
142
 
67
143
  ```bash
68
144
  imtoagent setup
69
145
  ```
70
146
 
71
- The interactive setup wizard guides you through:
147
+ The interactive wizard will guide you through:
72
148
 
73
- 1. **Configure Bot** โ€” Select IM platform + Agent backend
149
+ 1. **Configure Bot** โ€” Select IM platform + Agent backend combination
74
150
  2. **Configure Model Providers** โ€” Add API credentials (DeepSeek, Dashscope, etc.)
75
- 3. **Generate Soul Files** โ€” Create rules.md / identity.md etc. for each Bot
151
+ 3. **Generate Soul Files** โ€” Create personality files (rules.md, identity.md, etc.)
76
152
  4. **Write Config Files** โ€” Auto-generate `~/.imtoagent/config.json`
77
153
 
78
- #### Feishu Bot Requirements
154
+ ### Platform-Specific Requirements
79
155
 
80
- - Feishu App ID (`cli_...`)
81
- - Feishu App Secret
82
- - Feishu app must enable: Bot, Event Subscription, Message Send/Receive permissions
156
+ #### Feishu
157
+ - **App ID** (`cli_...`) and **App Secret**
158
+ - Enable in Feishu app console: Bot, Event Subscription, Message Send/Receive permissions
83
159
 
84
- #### Telegram Bot Requirements
85
-
86
- - Telegram Bot Token (obtain from @BotFather)
87
- - Optional: Proxy address (e.g., `http://127.0.0.1:7890`)
160
+ #### Telegram
161
+ - **Bot Token** from @BotFather
162
+ - Optional: HTTP proxy (e.g., `http://127.0.0.1:7890`)
88
163
 
89
164
  #### Personal WeChat
165
+ - QR code automatically appears on first `imtoagent start`
166
+ - Scan with WeChat on your phone to complete binding
90
167
 
91
- - QR code automatically pops up on first run of `imtoagent start`
92
- - Scan with your phone's WeChat to complete binding
168
+ #### WeCom (Enterprise WeChat)
169
+ - Webhook callback URL configuration
170
+ - REST API credentials
93
171
 
94
- ### Start the Gateway
172
+ ## Running the Gateway
95
173
 
96
- ```bash
97
- imtoagent start # Start in background
98
- imtoagent status # Check running status
99
- imtoagent stop # Stop the gateway
100
- ```
174
+ ### Basic Commands
175
+
176
+ | Command | Description |
177
+ |---------|-------------|
178
+ | `imtoagent start` | Start gateway in background |
179
+ | `imtoagent stop` | Stop the gateway |
180
+ | `imtoagent status` | Check running status |
181
+ | `imtoagent restore` | Hot reload recovery |
182
+ | `imtoagent daemon` | Foreground daemon mode (crash auto-restart) |
183
+
184
+ ### Running Modes
101
185
 
102
- ### Auto-Start on Boot (macOS launchd)
186
+ - **`start`** โ€” Background mode, terminal returns immediately
187
+ - **`run`** โ€” Foreground mode, real-time logs, Ctrl+C to stop
188
+ - **`daemon`** โ€” Foreground with auto-restart on crash
189
+
190
+ ### Auto-Start on Boot
191
+
192
+ #### macOS (launchd)
103
193
 
104
194
  ```bash
105
195
  # Create launchd configuration
@@ -134,36 +224,54 @@ cat > ~/Library/LaunchAgents/com.imtoagent.plist << 'EOF'
134
224
  </plist>
135
225
  EOF
136
226
 
137
- # Load
227
+ # Load the service
138
228
  launchctl load ~/Library/LaunchAgents/com.imtoagent.plist
139
229
  ```
140
230
 
141
- ### Common Commands
231
+ #### Linux (systemd)
142
232
 
143
- | Command | Description |
144
- |------|------|
145
- | `imtoagent setup` | Interactive setup wizard |
146
- | `imtoagent start` | Start gateway in background |
147
- | `imtoagent stop` | Stop the gateway |
148
- | `imtoagent status` | Check running status |
149
- | `imtoagent restore` | Hot reload recovery |
150
- | `imtoagent daemon` | Foreground daemon mode (suitable for launchd/systemd) |
233
+ Create `/etc/systemd/system/imtoagent.service`:
151
234
 
152
- ### Built-In Gateway Commands
235
+ ```ini
236
+ [Unit]
237
+ Description=IMtoAgent Gateway
238
+ After=network.target
153
239
 
154
- Send to the Bot in IM chat:
240
+ [Service]
241
+ Type=simple
242
+ User=youruser
243
+ WorkingDirectory=/home/youruser/.imtoagent
244
+ ExecStart=/usr/bin/bun run /usr/lib/node_modules/imtoagent/index.ts daemon
245
+ Restart=on-failure
246
+ RestartSec=5
247
+
248
+ [Install]
249
+ WantedBy=multi-user.target
250
+ ```
251
+
252
+ ```bash
253
+ systemctl daemon-reload
254
+ systemctl enable imtoagent
255
+ systemctl start imtoagent
256
+ ```
257
+
258
+ ## Using the Gateway
259
+
260
+ ### Built-In Commands
261
+
262
+ Send these to your Bot in the IM chat:
155
263
 
156
264
  | Command | Description |
157
- |------|------|
158
- | `/help` | Help information |
265
+ |---------|-------------|
266
+ | `/help` | Show available commands |
159
267
  | `/status` | Gateway status |
160
268
  | `/stats` | Usage statistics |
161
- | `/model` | Switch model |
162
- | `/providers` | View providers |
163
- | `/memory` | View memory |
269
+ | `/model` | Switch AI model |
270
+ | `/providers` | View model providers |
271
+ | `/memory` | View memory status |
164
272
  | `/soul` | Soul management |
165
- | `/reload` | Reload |
166
- | `/clear` | Clear session |
273
+ | `/reload` | Reload configuration |
274
+ | `/clear` | Clear conversation session |
167
275
  | `/mode` | Switch mode (permission/auto/plan) |
168
276
  | `/dir` | Switch working directory |
169
277
 
@@ -206,27 +314,66 @@ imtoagent/
206
314
 
207
315
  ## Data Directory
208
316
 
209
- All runtime data is stored centrally in `~/.imtoagent/`:
317
+ All runtime data is stored in `~/.imtoagent/`:
210
318
 
211
319
  ```
212
320
  ~/.imtoagent/
213
321
  โ”œโ”€โ”€ config.json # Main config (Bot + providers + system)
214
- โ”œโ”€โ”€ providers.json # Model provider config
215
- โ”œโ”€โ”€ opencode.json # OpenCode config
216
- โ”œโ”€โ”€ sessions/ # Session persistence
322
+ โ”œโ”€โ”€ providers.json # Model provider configurations
323
+ โ”œโ”€โ”€ opencode.json # OpenCode-specific config
324
+ โ”œโ”€โ”€ sessions/ # Conversation persistence
217
325
  โ”œโ”€โ”€ logs/ # Runtime logs
218
- โ””โ”€โ”€ soul/ # Soul files (one directory per Bot)
326
+ โ””โ”€โ”€ soul/ # Bot personality files
219
327
  โ”œโ”€โ”€ ClaudeBot/
220
328
  โ”œโ”€โ”€ CodexBot/
221
- โ””โ”€โ”€ ...
329
+ โ””โ”€โ”€ ... # One directory per Bot
330
+ ```
331
+
332
+ ## Troubleshooting
333
+
334
+ ### Common Issues
335
+
336
+ **Gateway won't start**
337
+ - Check `imtoagent status` for details
338
+ - Verify config with `cat ~/.imtoagent/config.json`
339
+ - Check logs: `cat ~/.imtoagent/logs/*.log`
340
+
341
+ **Setup wizard stuck**
342
+ - Ensure terminal supports interactive input
343
+ - Try running in a standard terminal (not IDE integrated)
344
+
345
+ **Bot not responding in IM**
346
+ - Verify credentials in config.json
347
+ - Check IM platform permissions (Feishu events, Telegram webhook, etc.)
348
+ - Ensure the gateway is running: `imtoagent status`
349
+
350
+ **Port 18899 already in use**
351
+ - Another service is using the proxy port
352
+ - Kill the existing process or change port in config
353
+
354
+ ### Getting Help
355
+
356
+ - Check logs: `~/.imtoagent/logs/`
357
+ - Run `imtoagent status` for runtime information
358
+ - Open an issue on GitHub with logs attached
222
359
  ```
223
360
 
224
361
  ## Development
225
362
 
226
363
  ```bash
364
+ # Clone and setup development environment
365
+ git clone https://github.com/imtoagent/imtoagent.git
366
+ cd imtoagent
227
367
  bun install
228
- bun run index.ts # Run directly
229
- bun run bin/imtoagent setup # Run setup wizard
368
+
369
+ # Run directly
370
+ bun run index.ts
371
+
372
+ # Run setup wizard
373
+ bun run bin/imtoagent setup
374
+
375
+ # Run CLI from source
376
+ bun run bin/imtoagent status
230
377
  ```
231
378
 
232
379
  ## License
@@ -5,14 +5,16 @@
5
5
  // Available after npm install -g imtoagent:
6
6
  // imtoagent setup โ€” interactive setup wizard
7
7
  // imtoagent start โ€” start gateway in background
8
+ // imtoagent run โ€” start gateway in foreground
8
9
  // imtoagent stop โ€” stop gateway
9
10
  // imtoagent status โ€” check running status
10
11
  // imtoagent restore โ€” hot reload
11
- // imtoagent daemon โ€” foreground daemon (auto-restart + logs)
12
+ // imtoagent daemon โ€” foreground daemon (auto-restart, for launchd/systemd)
12
13
  // ================================================================
13
14
 
14
15
  import * as fs from 'fs';
15
16
  import * as path from 'path';
17
+ import { spawn, execSync } from 'child_process';
16
18
  import { getDataDir } from '../modules/utils/paths';
17
19
 
18
20
  const PID_FILE = '/tmp/imtoagent.pid';
@@ -29,6 +31,9 @@ switch (command) {
29
31
  case 'start':
30
32
  await cmdStart();
31
33
  break;
34
+ case 'run':
35
+ await cmdRun();
36
+ break;
32
37
  case 'stop':
33
38
  await cmdStop();
34
39
  break;
@@ -42,14 +47,12 @@ switch (command) {
42
47
  await cmdDaemon();
43
48
  break;
44
49
  case undefined: {
45
- // No command โ†’ auto-enter setup if not configured, show help otherwise
46
50
  const dataDir = getDataDir();
47
51
  const configPath = path.join(dataDir, 'config.json');
48
52
  let needsSetup = !fs.existsSync(configPath);
49
53
  if (!needsSetup) {
50
54
  try {
51
55
  const raw = fs.readFileSync(configPath, 'utf-8');
52
- // If config still has YOUR_ placeholders, setup is incomplete
53
56
  needsSetup = /YOUR_[A-Z_]+/.test(raw);
54
57
  } catch { needsSetup = true; }
55
58
  }
@@ -81,11 +84,12 @@ imtoagent โ€” IM โ†” Agent Unified Gateway
81
84
 
82
85
  Usage:
83
86
  imtoagent setup Interactive setup wizard
84
- imtoagent start Start gateway in background
87
+ imtoagent start Start gateway in background (returns immediately)
88
+ imtoagent run Start gateway in foreground (Ctrl+C to stop)
85
89
  imtoagent stop Stop gateway
86
90
  imtoagent status Check running status
87
91
  imtoagent restore Hot reload
88
- imtoagent daemon Foreground daemon (auto-restart + logs, for launchd/systemd)
92
+ imtoagent daemon Foreground daemon with auto-restart (for launchd/systemd)
89
93
 
90
94
  Data directory: ${getDataDir()}
91
95
  `);
@@ -100,7 +104,22 @@ async function cmdSetup() {
100
104
  }
101
105
 
102
106
  // ================================================================
103
- // start โ€” launch in background
107
+ // Shared: build gateway launch args
108
+ // ================================================================
109
+ function getGatewayArgs() {
110
+ const pkgDir = path.resolve(import.meta.dirname, '..');
111
+ const indexFile = path.join(pkgDir, 'index.ts');
112
+ return { execPath: process.execPath, args: ['run', indexFile] };
113
+ }
114
+
115
+ function ensureLogDir(dataDir: string) {
116
+ const logsDir = path.join(dataDir, 'logs');
117
+ if (!fs.existsSync(logsDir)) fs.mkdirSync(logsDir, { recursive: true });
118
+ return path.join(logsDir, 'imtoagent.log');
119
+ }
120
+
121
+ // ================================================================
122
+ // start โ€” background mode (spawn detached, log to file, return immediately)
104
123
  // ================================================================
105
124
  async function cmdStart() {
106
125
  // Check if already running
@@ -112,12 +131,10 @@ async function cmdStart() {
112
131
  console.error(` Run "imtoagent stop" to stop first`);
113
132
  process.exit(1);
114
133
  } catch {
115
- // Stale PID file, clean up
116
134
  fs.unlinkSync(PID_FILE);
117
135
  }
118
136
  }
119
137
 
120
- // Check if config exists
121
138
  const dataDir = getDataDir();
122
139
  const configPath = path.join(dataDir, 'config.json');
123
140
  if (!fs.existsSync(configPath)) {
@@ -125,7 +142,7 @@ async function cmdStart() {
125
142
  process.exit(1);
126
143
  }
127
144
 
128
- // Check if configured backends are installed
145
+ // Backend check (non-blocking)
129
146
  try {
130
147
  const { checkBackend } = await import('../modules/utils/backend-check');
131
148
  const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
@@ -140,25 +157,89 @@ async function cmdStart() {
140
157
  }
141
158
  if (missingBackends.length > 0) {
142
159
  console.error(`\nโš ๏ธ The following backends are configured but not installed, messages will fail after gateway starts:`);
143
- for (const b of missingBackends) {
144
- console.error(` โŒ ${b}`);
145
- }
160
+ for (const b of missingBackends) console.error(` โŒ ${b}`);
146
161
  console.error(`\nPlease install the missing backends, or run "imtoagent setup" to reconfigure.\n`);
147
- // Don't force exit, let user start gateway and install backends later
148
162
  }
149
163
  } catch {
150
164
  // Check failure doesn't block startup
151
165
  }
152
166
 
153
- console.log('๐Ÿš€ Starting imtoagent gateway...');
167
+ const logFile = ensureLogDir(dataDir);
168
+
169
+ console.log('๐Ÿš€ Starting imtoagent gateway (background)...');
154
170
  console.log(` Data directory: ${dataDir}`);
155
- console.log(` Config file: ${configPath}`);
171
+ console.log(` Log file: ${logFile}`);
156
172
 
157
- // Launch in background using Bun.spawn
158
- const pkgDir = path.resolve(import.meta.dirname, '..');
159
- const indexFile = path.join(pkgDir, 'index.ts');
173
+ const { execPath, args } = getGatewayArgs();
174
+ const cmdLine = `"${execPath}" run "${path.resolve(import.meta.dirname, '..', 'index.ts')}"`;
175
+
176
+ // Use a shell to launch the gateway in background โ€” avoids event-loop blockers
177
+ const shellCmd = `IMTOAGENT_HOME="${dataDir}" ${cmdLine} >> "${logFile}" 2>&1 &
178
+ PID=$!
179
+ echo $PID`;
160
180
 
161
- const child = Bun.spawn([process.execPath, 'run', indexFile], {
181
+ const { execSync } = await import('child_process');
182
+ const pidStr = execSync(shellCmd, {
183
+ cwd: dataDir,
184
+ env: { ...process.env, IMTOAGENT_HOME: dataDir },
185
+ encoding: 'utf-8',
186
+ stdio: ['ignore', 'pipe', 'pipe'],
187
+ }).trim();
188
+ const gatewayPid = parseInt(pidStr.split('\n').pop()!);
189
+
190
+ fs.writeFileSync(PID_FILE, String(gatewayPid));
191
+ console.log(`โœ… Gateway started (PID=${gatewayPid})`);
192
+
193
+ // Wait for startup verification
194
+ await new Promise(r => setTimeout(r, 3000));
195
+ try {
196
+ process.kill(gatewayPid, 0);
197
+ console.log('โœ… Gateway is running');
198
+ } catch {
199
+ console.error('โŒ Gateway failed to start, check logs:');
200
+ if (fs.existsSync(logFile)) {
201
+ console.log(fs.readFileSync(logFile, 'utf-8').slice(-2000));
202
+ }
203
+ fs.unlinkSync(PID_FILE);
204
+ process.exit(1);
205
+ }
206
+
207
+ // Explicitly exit โ€” Bun may keep event loop alive due to inherited stdio
208
+ process.exit(0);
209
+ }
210
+
211
+ // ================================================================
212
+ // run โ€” foreground mode (real-time logs, Ctrl+C to stop)
213
+ // ================================================================
214
+ async function cmdRun() {
215
+ const dataDir = getDataDir();
216
+ const configPath = path.join(dataDir, 'config.json');
217
+ if (!fs.existsSync(configPath)) {
218
+ console.error('โŒ No config file found. Please run "imtoagent setup" first');
219
+ process.exit(1);
220
+ }
221
+
222
+ const logFile = ensureLogDir(dataDir);
223
+
224
+ // Warn if already running in background
225
+ if (fs.existsSync(PID_FILE)) {
226
+ const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim());
227
+ try {
228
+ process.kill(pid, 0);
229
+ console.log(`โš ๏ธ Gateway is already running in background (PID=${pid})`);
230
+ console.log(` Run "imtoagent stop" first, or this may conflict.\n`);
231
+ } catch {
232
+ fs.unlinkSync(PID_FILE);
233
+ }
234
+ }
235
+
236
+ const { execPath, args } = getGatewayArgs();
237
+
238
+ console.log('๐Ÿš€ Starting imtoagent gateway (foreground mode)...');
239
+ console.log(' Press Ctrl+C to stop');
240
+ console.log('');
241
+
242
+ const child = Bun.spawn([execPath, ...args], {
162
243
  cwd: dataDir,
163
244
  env: { ...process.env, IMTOAGENT_HOME: dataDir },
164
245
  stdout: 'pipe',
@@ -166,16 +247,10 @@ async function cmdStart() {
166
247
  });
167
248
 
168
249
  fs.writeFileSync(PID_FILE, String(child.pid));
169
- console.log(`โœ… Gateway started (PID=${child.pid})`);
170
250
 
171
- // Redirect background logs to logs/
172
- const logsDir = path.join(dataDir, 'logs');
173
- if (!fs.existsSync(logsDir)) fs.mkdirSync(logsDir, { recursive: true });
174
- const logFile = path.join(logsDir, 'imtoagent.log');
251
+ const logStream = fs.createWriteStream(logFile, { flags: 'a' });
175
252
 
176
- // Start log collection
177
- (async () => {
178
- const logStream = fs.createWriteStream(logFile, { flags: 'a' });
253
+ const pumpOut = (async () => {
179
254
  for await (const chunk of child.stdout as any) {
180
255
  const line = new TextDecoder().decode(chunk);
181
256
  process.stdout.write(line);
@@ -183,8 +258,7 @@ async function cmdStart() {
183
258
  }
184
259
  })().catch(() => {});
185
260
 
186
- (async () => {
187
- const logStream = fs.createWriteStream(logFile, { flags: 'a' });
261
+ const pumpErr = (async () => {
188
262
  for await (const chunk of child.stderr as any) {
189
263
  const line = new TextDecoder().decode(chunk);
190
264
  process.stderr.write(line);
@@ -192,18 +266,24 @@ async function cmdStart() {
192
266
  }
193
267
  })().catch(() => {});
194
268
 
195
- // Wait for startup verification (check PID survives 5s)
196
- await new Promise(r => setTimeout(r, 3000));
197
- try {
198
- process.kill(child.pid, 0);
199
- console.log('โœ… Gateway is running');
200
- } catch {
201
- console.error('โŒ Gateway failed to start, check logs:');
202
- if (fs.existsSync(logFile)) {
203
- console.log(fs.readFileSync(logFile, 'utf-8').slice(-2000));
204
- }
205
- fs.unlinkSync(PID_FILE);
206
- process.exit(1);
269
+ // Ctrl+C โ†’ SIGTERM to child
270
+ const cleanup = () => {
271
+ console.log('\n๐Ÿ›‘ Stopping gateway...');
272
+ try { process.kill(child.pid, 'SIGTERM'); } catch {}
273
+ };
274
+ process.on('SIGINT', cleanup);
275
+ process.on('SIGTERM', cleanup);
276
+
277
+ const exitCode = await child.exited;
278
+ await Promise.allSettled([pumpOut, pumpErr]);
279
+ logStream.end();
280
+
281
+ try { fs.unlinkSync(PID_FILE); } catch {}
282
+
283
+ if (exitCode === 0) {
284
+ console.log('โœ… Gateway exited cleanly');
285
+ } else {
286
+ console.log(`โš ๏ธ Gateway exited with code ${exitCode}`);
207
287
  }
208
288
  }
209
289
 
@@ -222,7 +302,6 @@ async function cmdStop() {
222
302
  console.log(`โน Stopping gateway (PID=${pid})...`);
223
303
  process.kill(pid, 'SIGTERM');
224
304
 
225
- // Wait for process to exit
226
305
  for (let i = 0; i < 20; i++) {
227
306
  try {
228
307
  process.kill(pid, 0);
@@ -232,7 +311,6 @@ async function cmdStop() {
232
311
  }
233
312
  }
234
313
 
235
- // Check if still running
236
314
  try {
237
315
  process.kill(pid, 0);
238
316
  console.log('โš ๏ธ Process not responding, force killing...');
@@ -256,7 +334,6 @@ async function cmdStatus() {
256
334
  console.log(`\n๐Ÿ“Š imtoagent Status`);
257
335
  console.log(` Data directory: ${dataDir}`);
258
336
 
259
- // Process status
260
337
  if (fs.existsSync(PID_FILE)) {
261
338
  const pid = parseInt(fs.readFileSync(PID_FILE, 'utf-8').trim());
262
339
  try {
@@ -269,7 +346,6 @@ async function cmdStatus() {
269
346
  console.log(` Process: โธ Not running`);
270
347
  }
271
348
 
272
- // Config file
273
349
  const configPath = path.join(dataDir, 'config.json');
274
350
  if (fs.existsSync(configPath)) {
275
351
  try {
@@ -286,7 +362,6 @@ async function cmdStatus() {
286
362
  console.log(` Config: โŒ Not found (run "imtoagent setup")`);
287
363
  }
288
364
 
289
- // Log file
290
365
  const logFile = path.join(dataDir, 'logs', 'imtoagent.log');
291
366
  if (fs.existsSync(logFile)) {
292
367
  const stats = fs.statSync(logFile);
@@ -320,13 +395,7 @@ async function cmdRestore() {
320
395
  }
321
396
 
322
397
  // ================================================================
323
- // daemon โ€” foreground daemon mode (auto-restart + logs + graceful shutdown)
324
- // ================================================================
325
- // Design:
326
- // - Runs in foreground, managed by launchd / systemd etc.
327
- // - Auto-restarts on crash (exponential backoff, max 30s)
328
- // - Graceful shutdown on SIGTERM/SIGINT, no restart
329
- // - Logs written to ~/.imtoagent/logs/imtoagent.log
398
+ // daemon โ€” foreground daemon with auto-restart (for launchd/systemd)
330
399
  // ================================================================
331
400
  async function cmdDaemon(): Promise<void> {
332
401
  const dataDir = getDataDir();
@@ -337,21 +406,15 @@ async function cmdDaemon(): Promise<void> {
337
406
  process.exit(1);
338
407
  }
339
408
 
340
- const logsDir = path.join(dataDir, 'logs');
341
- if (!fs.existsSync(logsDir)) fs.mkdirSync(logsDir, { recursive: true });
342
- const logFile = path.join(logsDir, 'imtoagent.log');
343
-
344
- const pkgDir = path.resolve(import.meta.dirname, '..');
345
- const indexFile = path.join(pkgDir, 'index.ts');
409
+ const logFile = ensureLogDir(dataDir);
410
+ const { execPath, args } = getGatewayArgs();
346
411
 
347
412
  console.log(`๐Ÿ›ก imtoagent Daemon Mode`);
348
413
  console.log(` Data directory: ${dataDir}`);
349
414
  console.log(` Log file: ${logFile}`);
350
415
  console.log(` Press Ctrl+C to stop\n`);
351
416
 
352
- // Graceful shutdown flag
353
417
  let shuttingDown = false;
354
-
355
418
  const shutdown = () => {
356
419
  if (shuttingDown) return;
357
420
  shuttingDown = true;
@@ -362,15 +425,13 @@ async function cmdDaemon(): Promise<void> {
362
425
  process.on('SIGINT', shutdown);
363
426
 
364
427
  let retryDelay = 0;
365
- const MAX_RETRY_DELAY = 30_000; // 30s cap
428
+ const MAX_RETRY_DELAY = 30_000;
366
429
 
367
430
  while (!shuttingDown) {
368
- // No delay on first run, exponential backoff after
369
431
  if (retryDelay > 0) {
370
432
  console.log(` Waiting ${retryDelay / 1000}s before restart...`);
371
433
  await new Promise<void>(resolve => {
372
434
  const timer = setTimeout(resolve, retryDelay);
373
- // Exit immediately if shutdown signal received during wait
374
435
  const check = setInterval(() => {
375
436
  if (shuttingDown) {
376
437
  clearTimeout(timer);
@@ -382,56 +443,39 @@ async function cmdDaemon(): Promise<void> {
382
443
  if (shuttingDown) break;
383
444
  }
384
445
 
385
- const logStream = fs.createWriteStream(logFile, { flags: 'a' });
446
+ // Open log fd for child stdout/stderr
447
+ const logFd = fs.openSync(logFile, 'a');
386
448
 
387
- const child = Bun.spawn([process.execPath, 'run', indexFile], {
449
+ const child = spawn(execPath, args, {
388
450
  cwd: dataDir,
389
451
  env: { ...process.env, IMTOAGENT_HOME: dataDir },
390
- stdout: 'pipe',
391
- stderr: 'pipe',
452
+ detached: true,
453
+ stdio: ['ignore', logFd, logFd],
392
454
  });
393
455
 
394
456
  const childPid = child.pid;
395
457
  fs.writeFileSync(PID_FILE, String(childPid));
396
458
  console.log(`[${new Date().toISOString()}] ๐Ÿš€ Starting gateway (PID=${childPid})`);
397
459
 
398
- // Log collection
399
- const pumpStdout = (async () => {
400
- for await (const chunk of child.stdout as any) {
401
- const line = new TextDecoder().decode(chunk);
402
- process.stdout.write(line);
403
- logStream.write(line);
404
- }
405
- })().catch(() => {});
406
-
407
- const pumpStderr = (async () => {
408
- for await (const chunk of child.stderr as any) {
409
- const line = new TextDecoder().decode(chunk);
410
- process.stderr.write(line);
411
- logStream.write(line);
412
- }
413
- })().catch(() => {});
414
-
415
- // Wait for child process to exit
416
- const exitCode = await child.exited;
417
- await Promise.allSettled([pumpStdout, pumpStderr]);
418
- logStream.end();
460
+ let childExitCode: number | null = null;
461
+ await new Promise<void>(resolve => {
462
+ child.on('exit', (code) => { childExitCode = code; resolve(); });
463
+ child.on('error', () => resolve());
464
+ });
419
465
 
466
+ fs.closeSync(logFd);
420
467
  try { fs.unlinkSync(PID_FILE); } catch {}
421
468
 
422
469
  if (shuttingDown) break;
423
470
 
424
- // Determine if restart needed
425
- if (exitCode === 0) {
471
+ if (childExitCode === 0) {
426
472
  console.log(`[${new Date().toISOString()}] โน Gateway exited cleanly (code=0), not restarting`);
427
473
  break;
428
474
  }
429
475
 
430
- // Crash โ†’ exponential backoff restart
431
476
  retryDelay = retryDelay === 0 ? 3_000 : Math.min(retryDelay * 2, MAX_RETRY_DELAY);
432
- console.log(`[${new Date().toISOString()}] โš ๏ธ Gateway crashed (code=${exitCode}), restarting in ${retryDelay / 1000}s`);
477
+ console.log(`[${new Date().toISOString()}] โš ๏ธ Gateway crashed (code=${childExitCode}), restarting in ${retryDelay / 1000}s`);
433
478
  }
434
479
 
435
480
  console.log('๐Ÿ‘‹ Daemon stopped');
436
481
  }
437
-
package/bin/imtoagent.cjs CHANGED
@@ -3,7 +3,7 @@
3
3
  "use strict";
4
4
  var path = require("path");
5
5
  var fs = require("fs");
6
- var spawnSync = require("child_process").spawnSync;
6
+ var spawn = require("child_process").spawn;
7
7
 
8
8
  var candidates = [
9
9
  process.env.BUN_BIN,
@@ -12,7 +12,7 @@ var candidates = [
12
12
  "/opt/homebrew/bin/bun",
13
13
  ];
14
14
  try {
15
- var r = spawnSync("which", ["bun"]);
15
+ var r = require("child_process").spawnSync("which", ["bun"]);
16
16
  if (r.status === 0) candidates.unshift(r.stdout.toString().trim());
17
17
  } catch (e) {}
18
18
 
@@ -32,8 +32,16 @@ if (!bunPath) {
32
32
 
33
33
  var pkgDir = path.resolve(__dirname, "..");
34
34
  var real = path.join(pkgDir, "bin", "imtoagent-real");
35
- var result = spawnSync(bunPath, [real].concat(process.argv.slice(2)), {
35
+ var child = spawn(bunPath, [real].concat(process.argv.slice(2)), {
36
36
  stdio: "inherit",
37
37
  env: Object.assign({}, process.env),
38
38
  });
39
- process.exit(result.status || 0);
39
+
40
+ child.on("exit", function (code) {
41
+ process.exit(code || 0);
42
+ });
43
+
44
+ child.on("error", function (err) {
45
+ console.error("โŒ Failed to start imtoagent:", err.message);
46
+ process.exit(1);
47
+ });
package/install.sh ADDED
@@ -0,0 +1,313 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # imtoagent โ€” One-Click Install Script
4
+ # Usage: curl -fsSL https://raw.githubusercontent.com/imtoagent/imtoagent/main/scripts/install.sh | bash
5
+ #
6
+ # This script detects the environment, installs dependencies, installs/upgrades
7
+ # imtoagent, and optionally runs the interactive setup wizard.
8
+ #
9
+ # Flags:
10
+ # --non-interactive Skip interactive setup (for CI/automated installs)
11
+ # --skip-bun Skip bun installation check
12
+ # --skip-start Don't start the gateway after install
13
+
14
+ set -euo pipefail
15
+
16
+ # ============================================================
17
+ # Colors & Helpers
18
+ # ============================================================
19
+ RED='\033[0;31m'
20
+ GREEN='\033[0;32m'
21
+ YELLOW='\033[1;33m'
22
+ BLUE='\033[0;34m'
23
+ CYAN='\033[0;36m'
24
+ BOLD='\033[1m'
25
+ NC='\033[0m'
26
+
27
+ info() { echo -e "${BLUE}โ„น ${NC}$1"; }
28
+ ok() { echo -e "${GREEN}โœ… ${NC}$1"; }
29
+ warn() { echo -e "${YELLOW}โš ๏ธ ${NC}$1"; }
30
+ error() { echo -e "${RED}โŒ ${NC}$1"; }
31
+ step() { echo -e "\n${BOLD}${CYAN}โ–ธ $1${NC}"; }
32
+ done_ok() { echo -e " ${GREEN}โœ“${NC} $1"; }
33
+
34
+ # ============================================================
35
+ # Parse flags
36
+ # ============================================================
37
+ NON_INTERACTIVE=false
38
+ SKIP_BUN=false
39
+ SKIP_START=false
40
+
41
+ for arg in "$@"; do
42
+ case "$arg" in
43
+ --non-interactive) NON_INTERACTIVE=true ;;
44
+ --skip-bun) SKIP_BUN=true ;;
45
+ --skip-start) SKIP_START=true ;;
46
+ esac
47
+ done
48
+
49
+ # ============================================================
50
+ # Banner
51
+ # ============================================================
52
+ echo ""
53
+ echo -e "${BOLD} โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”${NC}"
54
+ echo -e "${BOLD} โ”‚ ${CYAN}imtoagent${NC} โ€” IM โ†” Agent Unified Gateway${BOLD} โ”‚${NC}"
55
+ echo -e "${BOLD} โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜${NC}"
56
+ echo ""
57
+
58
+ # ============================================================
59
+ # 1. OS Detection
60
+ # ============================================================
61
+ step "1. Detecting environment"
62
+
63
+ OS=""
64
+ ARCH=""
65
+
66
+ case "$(uname -s)" in
67
+ Darwin*) OS="macos" ;;
68
+ Linux*) OS="linux" ;;
69
+ *) error "Unsupported OS: $(uname -s)"; exit 1 ;;
70
+ esac
71
+
72
+ case "$(uname -m)" in
73
+ arm64|aarch64) ARCH="aarch64" ;;
74
+ x86_64|amd64) ARCH="x64" ;;
75
+ *) ARCH="unknown" ;;
76
+ esac
77
+
78
+ done_ok "OS: $OS ($(uname -m))"
79
+
80
+ # ============================================================
81
+ # 2. Bun Detection & Installation
82
+ # ============================================================
83
+ if [ "$SKIP_BUN" = true ]; then
84
+ info "Skipping bun check (--skip-bun)"
85
+ else
86
+ step "2. Checking bun"
87
+
88
+ BUN_BIN=""
89
+
90
+ # Check environment variable first
91
+ if [ -n "${BUN_BIN:-}" ] && [ -x "$BUN_BIN" ]; then
92
+ BUN_BIN="$BUN_BIN"
93
+ # Check common paths
94
+ elif [ -x "$HOME/.bun/bin/bun" ]; then
95
+ BUN_BIN="$HOME/.bun/bin/bun"
96
+ elif [ -x "/opt/homebrew/bin/bun" ]; then
97
+ BUN_BIN="/opt/homebrew/bin/bun"
98
+ elif [ -x "/usr/local/bin/bun" ]; then
99
+ BUN_BIN="/usr/local/bin/bun"
100
+ elif command -v bun &>/dev/null; then
101
+ BUN_BIN="$(command -v bun)"
102
+ fi
103
+
104
+ if [ -n "$BUN_BIN" ]; then
105
+ BUN_VER=$("$BUN_BIN" --version 2>/dev/null || echo "unknown")
106
+ done_ok "bun found: $BUN_BIN (v${BUN_VER})"
107
+ else
108
+ warn "bun not found โ€” installing..."
109
+ echo ""
110
+
111
+ if [ "$OS" = "macos" ] || [ "$OS" = "linux" ]; then
112
+ echo " Downloading and installing bun..."
113
+ curl -fsSL https://bun.sh/install | bash 2>&1 | tail -5
114
+
115
+ # Add bun to PATH for this session
116
+ if [ -f "$HOME/.bun/bin/bun" ]; then
117
+ BUN_BIN="$HOME/.bun/bin/bun"
118
+ export PATH="$HOME/.bun/bin:$PATH"
119
+ elif command -v bun &>/dev/null; then
120
+ BUN_BIN="$(command -v bun)"
121
+ fi
122
+
123
+ if [ -n "$BUN_BIN" ] && [ -x "$BUN_BIN" ]; then
124
+ BUN_VER=$("$BUN_BIN" --version 2>/dev/null || echo "unknown")
125
+ done_ok "bun installed: v${BUN_VER}"
126
+ else
127
+ error "bun installation failed. Please install manually:"
128
+ echo " curl -fsSL https://bun.sh/install | bash"
129
+ echo " Then re-run this script."
130
+ exit 1
131
+ fi
132
+ else
133
+ error "Unsupported platform for automatic bun install."
134
+ echo " Install bun manually: https://bun.sh"
135
+ exit 1
136
+ fi
137
+ fi
138
+ fi
139
+
140
+ # ============================================================
141
+ # 3. Node.js Check (npm requires node)
142
+ # ============================================================
143
+ step "3. Checking node/npm"
144
+
145
+ if command -v node &>/dev/null; then
146
+ NODE_VER=$(node --version)
147
+ done_ok "node: ${NODE_VER}"
148
+ else
149
+ warn "node not found โ€” npm install will fail"
150
+ echo ""
151
+ echo " You need Node.js installed. Options:"
152
+ echo " macOS: brew install node"
153
+ echo " Linux: apt install nodejs npm (or use nvm)"
154
+ echo ""
155
+ echo " Or install via nvm:"
156
+ echo " curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash"
157
+ echo " source ~/.bashrc # or ~/.zshrc"
158
+ echo " nvm install 20"
159
+ echo ""
160
+ if [ "$NON_INTERACTIVE" = true ]; then
161
+ error "Cannot continue in non-interactive mode without node."
162
+ exit 1
163
+ fi
164
+ read -rp " Continue anyway? [y/N] " CONTINUE
165
+ case "$CONTINUE" in
166
+ [yY]*) info "Continuing..." ;;
167
+ *) error "Aborted."; exit 1 ;;
168
+ esac
169
+ fi
170
+
171
+ if command -v npm &>/dev/null; then
172
+ NPM_VER=$(npm --version)
173
+ done_ok "npm: v${NPM_VER}"
174
+ else
175
+ error "npm not found โ€” required for installation"
176
+ exit 1
177
+ fi
178
+
179
+ # ============================================================
180
+ # 4. Install / Upgrade imtoagent
181
+ # ============================================================
182
+ step "4. Installing imtoagent"
183
+
184
+ # Check if already installed
185
+ EXISTING_VER=""
186
+ if command -v imtoagent &>/dev/null; then
187
+ EXISTING_VER=$(imtoagent --version 2>/dev/null || echo "unknown")
188
+ warn "Already installed: v${EXISTING_VER}"
189
+ echo ""
190
+ if [ "$NON_INTERACTIVE" = true ]; then
191
+ info "Non-interactive mode โ€” upgrading"
192
+ else
193
+ read -rp " Upgrade to latest? [Y/n] " UPGRADE
194
+ case "$UPGRADE" in
195
+ [nN]*) info "Skipping upgrade."; exit 0 ;;
196
+ esac
197
+ fi
198
+ fi
199
+
200
+ echo " Running: npm install -g imtoagent"
201
+ echo ""
202
+ npm install -g imtoagent 2>&1 | tail -10
203
+
204
+ INSTALLED_VER=$(imtoagent --version 2>/dev/null || echo "unknown")
205
+ done_ok "imtoagent v${INSTALLED_VER} installed globally"
206
+
207
+ # ============================================================
208
+ # 5. Configuration Check & Setup
209
+ # ============================================================
210
+ step "5. Checking configuration"
211
+
212
+ CONFIG_DIR="$HOME/.imtoagent"
213
+ CONFIG_FILE="$CONFIG_DIR/config.json"
214
+
215
+ NEED_SETUP=true
216
+
217
+ if [ -f "$CONFIG_FILE" ]; then
218
+ done_ok "Config found: $CONFIG_FILE"
219
+
220
+ # Quick check if config has any bots (grep for botType field)
221
+ if grep -q '"botType"' "$CONFIG_FILE" 2>/dev/null; then
222
+ BOT_COUNT=$(grep -c '"botType"' "$CONFIG_FILE" 2>/dev/null || echo "0")
223
+ done_ok "$BOT_COUNT bot(s) configured โ€” skipping setup wizard"
224
+ NEED_SETUP=false
225
+ else
226
+ warn "No bots configured in config"
227
+ fi
228
+ else
229
+ warn "No configuration found"
230
+ fi
231
+
232
+ if [ "$NEED_SETUP" = true ]; then
233
+ if [ "$NON_INTERACTIVE" = true ]; then
234
+ info "Non-interactive mode โ€” skipping setup wizard"
235
+ echo ""
236
+ echo " Run 'imtoagent setup' manually to configure."
237
+ else
238
+ echo ""
239
+ echo " Starting interactive setup wizard..."
240
+ echo " You'll need:"
241
+ echo " โ€ข IM platform credentials (Feishu App ID/Secret, Telegram Token, etc.)"
242
+ echo " โ€ข Agent backend (Claude Code / Codex / OpenCode)"
243
+ echo " โ€ข Model provider API keys"
244
+ echo ""
245
+ read -rp " Start setup now? [Y/n] " START_SETUP
246
+ case "$START_SETUP" in
247
+ [nN]*)
248
+ info "Skipping setup. Run 'imtoagent setup' later to configure."
249
+ ;;
250
+ *)
251
+ echo ""
252
+ imtoagent setup
253
+ ;;
254
+ esac
255
+ fi
256
+ fi
257
+
258
+ # ============================================================
259
+ # 6. Start Gateway (optional)
260
+ # ============================================================
261
+ if [ "$SKIP_START" = true ]; then
262
+ info "Skipping gateway start (--skip-start)"
263
+ else
264
+ step "6. Starting gateway"
265
+
266
+ # Check if already running
267
+ if imtoagent status 2>/dev/null | grep -q "running"; then
268
+ warn "Gateway already running"
269
+ imtoagent status 2>/dev/null || true
270
+ else
271
+ if [ "$NON_INTERACTIVE" = true ]; then
272
+ info "Non-interactive mode โ€” starting gateway in background"
273
+ imtoagent start
274
+ sleep 3
275
+ imtoagent status 2>/dev/null || true
276
+ else
277
+ read -rp " Start gateway now? [Y/n] " START_GATEWAY
278
+ case "$START_GATEWAY" in
279
+ [nN]*)
280
+ info "Not starting. Run 'imtoagent start' when ready."
281
+ ;;
282
+ *)
283
+ imtoagent start
284
+ sleep 3
285
+ imtoagent status 2>/dev/null || true
286
+ ;;
287
+ esac
288
+ fi
289
+ fi
290
+ fi
291
+
292
+ # ============================================================
293
+ # Summary
294
+ # ============================================================
295
+ echo ""
296
+ echo -e "${BOLD} โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€${NC}"
297
+ echo -e "${GREEN}${BOLD} Installation Complete!${NC}"
298
+ echo -e "${BOLD} โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€${NC}"
299
+ echo ""
300
+ echo " Version: v${INSTALLED_VER}"
301
+ echo " Config: ${CONFIG_FILE}"
302
+ echo " Logs: ${CONFIG_DIR}/logs/"
303
+ echo ""
304
+ echo " Quick commands:"
305
+ echo " imtoagent status # Check gateway status"
306
+ echo " imtoagent stop # Stop the gateway"
307
+ echo " imtoagent setup # Configure bots"
308
+ echo " imtoagent restore # Hot reload"
309
+ echo ""
310
+ echo " Send ${BOLD}/help${NC} to your Bot in IM to see available commands."
311
+ echo ""
312
+ echo " Docs: https://github.com/imtoagent/imtoagent"
313
+ echo ""
@@ -88,7 +88,7 @@ async function spawnCodexResume(cwd: string, threadId: string, prompt: string):
88
88
  try {
89
89
  [stdout, stderr] = await Promise.all([
90
90
  new Response(child.stdout).text().catch((e: any) => { throw new Error(`stdout read failed: ${e?.message || e}`); }),
91
- new Response(child.stderr).text().catch((e: any) => { throw new Error(`stderr read failed: ${e?.message || e}`; }),
91
+ new Response(child.stderr).text().catch((e: any) => { throw new Error(`stderr read failed: ${e?.message || e}`); }),
92
92
  ]);
93
93
  } catch (ioErr: any) {
94
94
  try { child.kill('SIGKILL'); } catch {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imtoagent",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "IM โ†” Agent ็ปŸไธ€็ฝ‘ๅ…ณ โ€” ้ฃžไนฆ/Telegram/ๅพฎไฟก/ไผไธšๅพฎไฟกๅฏนๆŽฅ Claude Code/Codex/OpenCode",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,6 +13,7 @@
13
13
  "templates/",
14
14
  "scripts/",
15
15
  "README.md",
16
+ "install.sh",
16
17
  "bin/imtoagent.ts",
17
18
  "bin/imtoagent.cjs",
18
19
  "bin/imtoagent-real"
@@ -32,7 +33,8 @@
32
33
  },
33
34
  "repository": {
34
35
  "type": "git",
35
- "url": "git+https://github.com/YOUR_USERNAME/imtoagent.git"
36
+ "url": "https://github.com/imtoagent/imtoagent.git",
37
+ "directory": ""
36
38
  },
37
39
  "keywords": [
38
40
  "im",
@@ -50,7 +52,7 @@
50
52
  "author": "Keyi",
51
53
  "license": "MIT",
52
54
  "bugs": {
53
- "url": "https://github.com/YOUR_USERNAME/imtoagent/issues"
55
+ "url": "https://github.com/imtoagent/imtoagent/issues"
54
56
  },
55
- "homepage": "https://github.com/YOUR_USERNAME/imtoagent#readme"
57
+ "homepage": "https://github.com/imtoagent/imtoagent#readme"
56
58
  }