deckide 3.5.37 → 3.5.39

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/dist/server.js CHANGED
@@ -51,6 +51,14 @@ export async function createServer() {
51
51
  const decks = new Map();
52
52
  const terminals = new Map();
53
53
  const browserService = new AgentBrowserService();
54
+ // 共有ブラウザを起動時から常駐させ、BrowserPane を開いていなくても
55
+ // Codex/Claude の chrome MCP(--browserUrl 127.0.0.1:9222)が常に使えるようにする。
56
+ // 失敗してもサーバ起動は継続(Chrome 未導入の環境などを考慮)。AGENT_BROWSER_AUTOSTART=false で無効化可。
57
+ if (process.env.AGENT_BROWSER_AUTOSTART !== 'false') {
58
+ void browserService.start().catch((err) => {
59
+ console.error('[browser] 共有ブラウザの自動起動に失敗:', err instanceof Error ? err.message : err);
60
+ });
61
+ }
54
62
  loadPersistedState(db, workspaces, workspacePathIndex, decks);
55
63
  // Create Hono app
56
64
  const app = new Hono();
@@ -280,6 +280,13 @@ export class AgentBrowserService {
280
280
  launching = null;
281
281
  port = null;
282
282
  executablePath = null;
283
+ // 共有ブラウザを常駐させるための状態。keepAlive が true の間はクラッシュ時に
284
+ // 自動再起動し、9222 を常時稼働させる(BrowserPane を開いていなくても
285
+ // chrome MCP が使えるようにするため)。明示的な stop() で false にする。
286
+ keepAlive = false;
287
+ restartTimer = null;
288
+ restartAttempts = 0;
289
+ lastStartTime = 0;
283
290
  // Browser-level CDP connection used only to discover tabs (page targets) and
284
291
  // drive their lifecycle (create/close/activate). One per running Chrome.
285
292
  browserCdp = null;
@@ -333,6 +340,8 @@ export class AgentBrowserService {
333
340
  };
334
341
  }
335
342
  async start() {
343
+ // 一度起動を要求されたら常駐させる(クラッシュ時は自動再起動)。
344
+ this.keepAlive = true;
336
345
  if (this.isRunning()) {
337
346
  await this.ensureSession();
338
347
  if (this.clients.size > 0) {
@@ -350,6 +359,7 @@ export class AgentBrowserService {
350
359
  this.launching = this.launch();
351
360
  try {
352
361
  await this.launching;
362
+ this.lastStartTime = Date.now();
353
363
  }
354
364
  catch (error) {
355
365
  this.lastError = error instanceof Error ? error.message : String(error);
@@ -364,6 +374,12 @@ export class AgentBrowserService {
364
374
  }
365
375
  }
366
376
  async stop() {
377
+ // 明示的な停止 → 自動再起動を無効化。
378
+ this.keepAlive = false;
379
+ if (this.restartTimer) {
380
+ clearTimeout(this.restartTimer);
381
+ this.restartTimer = null;
382
+ }
367
383
  this.disconnectActiveCdp();
368
384
  this.closeBrowserCdp();
369
385
  const proc = this.chromeProcess;
@@ -438,6 +454,49 @@ export class AgentBrowserService {
438
454
  await this.refreshActivePageInfo();
439
455
  await this.broadcastStatus();
440
456
  }
457
+ // クラッシュ後に共有ブラウザを自動再起動する(9222 常時稼働の維持)。
458
+ // 直近の起動が短命なら連続再起動を抑制し、5 回連続で短命なら諦める。
459
+ scheduleAutoRestart() {
460
+ if (this.restartTimer || !this.keepAlive) {
461
+ return;
462
+ }
463
+ // 30 秒以上動いていたら安定とみなしてカウンタをリセット。
464
+ if (Date.now() - this.lastStartTime > 30_000) {
465
+ this.restartAttempts = 0;
466
+ }
467
+ if (this.restartAttempts >= 5) {
468
+ console.error('[agent-browser] 自動再起動を中止(短時間に連続クラッシュ)');
469
+ this.keepAlive = false;
470
+ return;
471
+ }
472
+ this.restartAttempts++;
473
+ const delay = Math.min(1000 * this.restartAttempts, 10_000);
474
+ this.restartTimer = setTimeout(() => {
475
+ this.restartTimer = null;
476
+ if (!this.keepAlive)
477
+ return;
478
+ this.start().catch((e) => {
479
+ console.error('[agent-browser] 自動再起動に失敗:', e instanceof Error ? e.message : e);
480
+ });
481
+ }, delay);
482
+ this.restartTimer.unref?.();
483
+ }
484
+ // 履歴を delta だけ移動(-1=戻る, +1=進む)。端なら何もしない。
485
+ async goHistory(delta) {
486
+ await this.start();
487
+ await this.ensureActiveCdp();
488
+ const cdp = this.activeCdp;
489
+ if (!cdp)
490
+ return;
491
+ const history = await cdp.send('Page.getNavigationHistory');
492
+ const targetIndex = history.currentIndex + delta;
493
+ if (targetIndex < 0 || targetIndex >= history.entries.length) {
494
+ return; // これ以上戻る/進む先がない
495
+ }
496
+ await cdp.send('Page.navigateToHistoryEntry', { entryId: history.entries[targetIndex].id });
497
+ await this.refreshActivePageInfo();
498
+ await this.broadcastStatus();
499
+ }
441
500
  async newTab(input) {
442
501
  await this.start();
443
502
  await this.connectBrowserCdp();
@@ -525,6 +584,10 @@ export class AgentBrowserService {
525
584
  this.closeLogStream();
526
585
  this.lastError = code === 0 ? undefined : `Browser exited (${signal ?? code ?? 'unknown'})`;
527
586
  void this.broadcastStatus();
587
+ // 明示停止でなく落ちた(クラッシュ)なら、常駐維持のため自動再起動。
588
+ if (this.keepAlive) {
589
+ this.scheduleAutoRestart();
590
+ }
528
591
  }
529
592
  });
530
593
  child.once('error', (error) => {
@@ -929,6 +992,20 @@ export class AgentBrowserService {
929
992
  await this.navigate(message.url);
930
993
  return;
931
994
  }
995
+ if (message.type === 'back') {
996
+ await this.goHistory(-1);
997
+ return;
998
+ }
999
+ if (message.type === 'forward') {
1000
+ await this.goHistory(1);
1001
+ return;
1002
+ }
1003
+ if (message.type === 'reload') {
1004
+ await this.activeCdp?.send('Page.reload', {});
1005
+ await this.refreshActivePageInfo();
1006
+ await this.broadcastStatus();
1007
+ return;
1008
+ }
932
1009
  if (message.type === 'mouse') {
933
1010
  await this.dispatchMouse(message);
934
1011
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deckide",
3
- "version": "3.5.37",
3
+ "version": "3.5.39",
4
4
  "description": "Deck IDE - Browser-based IDE with terminal, file explorer, and git integration",
5
5
  "type": "module",
6
6
  "bin": {