wtt-connect 0.2.41 → 0.2.42

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/main.js +84 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wtt-connect",
3
- "version": "0.2.41",
3
+ "version": "0.2.42",
4
4
  "private": false,
5
5
  "description": "WTT-native connector daemon for Codex, Claude Code, Cursor, Gemini, ACP, and other coding agent surfaces.",
6
6
  "type": "module",
package/src/main.js CHANGED
@@ -237,21 +237,90 @@ function writePreviewRegistry(config, registry) {
237
237
 
238
238
  function stopPreviewPort(port) {
239
239
  if (!Number.isInteger(port) || port < 1024 || port > 65535) return { killed: [], reason: 'invalid_port' };
240
- const script = [
241
- `pids=$(ss -ltnp 2>/dev/null | awk '/:${port} / {print $NF}' | sed -n 's/.*pid=\\([0-9][0-9]*\\).*/\\1/p' | sort -u)`,
242
- 'killed=""',
243
- 'for pid in $pids; do',
244
- ' if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then kill "$pid" 2>/dev/null || true; killed="$killed $pid"; fi',
245
- 'done',
246
- 'sleep 0.2',
247
- 'for pid in $pids; do',
248
- ' if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then kill -9 "$pid" 2>/dev/null || true; fi',
249
- 'done',
250
- 'printf "%s" "$killed"',
251
- ].join('; ');
252
- const result = spawnSync('bash', ['-lc', script], { encoding: 'utf8', timeout: 5000 });
253
- const killed = String(result.stdout || '').trim().split(/\s+/).filter(Boolean);
254
- return { killed, ok: result.status === 0 };
240
+ const pids = listenerPidsForPort(port);
241
+ const killed = [];
242
+ for (const pid of pids) {
243
+ try {
244
+ process.kill(pid, 'SIGTERM');
245
+ killed.push(String(pid));
246
+ } catch {}
247
+ }
248
+ const deadline = Date.now() + 500;
249
+ while (Date.now() < deadline && pids.some((pid) => isProcessAlive(pid))) {
250
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 50);
251
+ }
252
+ for (const pid of pids) {
253
+ if (!isProcessAlive(pid)) continue;
254
+ try {
255
+ process.kill(pid, 'SIGKILL');
256
+ if (!killed.includes(String(pid))) killed.push(String(pid));
257
+ } catch {}
258
+ }
259
+ return { killed, ok: true };
260
+ }
261
+
262
+ function listenerPidsForPort(port) {
263
+ const inodes = listenerInodesForPort(port);
264
+ if (!inodes.size) return [];
265
+ const pids = [];
266
+ for (const name of safeReadDir('/proc')) {
267
+ if (!/^\d+$/.test(name)) continue;
268
+ const fdDir = `/proc/${name}/fd`;
269
+ for (const fd of safeReadDir(fdDir)) {
270
+ let target = '';
271
+ try {
272
+ target = fs.readlinkSync(path.join(fdDir, fd));
273
+ } catch {
274
+ continue;
275
+ }
276
+ const match = target.match(/^socket:\[(\d+)\]$/);
277
+ if (match && inodes.has(match[1])) {
278
+ pids.push(Number(name));
279
+ break;
280
+ }
281
+ }
282
+ }
283
+ return [...new Set(pids)];
284
+ }
285
+
286
+ function listenerInodesForPort(port) {
287
+ const targetHex = Number(port).toString(16).toUpperCase().padStart(4, '0');
288
+ const inodes = new Set();
289
+ for (const file of ['/proc/net/tcp', '/proc/net/tcp6']) {
290
+ let text = '';
291
+ try {
292
+ text = fs.readFileSync(file, 'utf8');
293
+ } catch {
294
+ continue;
295
+ }
296
+ for (const line of text.split('\n').slice(1)) {
297
+ const parts = line.trim().split(/\s+/);
298
+ if (parts.length < 10) continue;
299
+ const local = parts[1] || '';
300
+ const state = parts[3] || '';
301
+ const inode = parts[9] || '';
302
+ const localPort = local.split(':')[1] || '';
303
+ if (state === '0A' && localPort.toUpperCase() === targetHex && inode) inodes.add(inode);
304
+ }
305
+ }
306
+ return inodes;
307
+ }
308
+
309
+ function safeReadDir(dir) {
310
+ try {
311
+ return fs.readdirSync(dir);
312
+ } catch {
313
+ return [];
314
+ }
315
+ }
316
+
317
+ function isProcessAlive(pid) {
318
+ try {
319
+ process.kill(pid, 0);
320
+ return true;
321
+ } catch {
322
+ return false;
323
+ }
255
324
  }
256
325
 
257
326
  async function createSandboxPreviewFromOutbox(config, port, { name = '', token = '', timeoutMs = 15000 } = {}) {