pinokiod 7.1.13 → 7.1.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "7.1.13",
3
+ "version": "7.1.14",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -970,12 +970,11 @@ class Server {
970
970
  console.log("Chrome")
971
971
 
972
972
  let name = req.params.name
973
- const binCheckLabel = `bin check ${name} ${process.hrtime.bigint()}`
974
- console.time(binCheckLabel)
973
+ console.time("bin check")
975
974
  let { requirements, install_required, requirements_pending, error } = await this.kernel.bin.check({
976
975
  bin: this.kernel.bin.preset("dev"),
977
976
  })
978
- console.timeEnd(binCheckLabel)
977
+ console.timeEnd("bin check")
979
978
  if (!requirements_pending && install_required) {
980
979
  res.redirect(`/setup/dev?callback=${req.originalUrl}`)
981
980
  return
@@ -4765,8 +4764,12 @@ class Server {
4765
4764
  // }
4766
4765
  }
4767
4766
  }
4768
- async terminals(filepath) {
4769
- let venvs = await Util.find_venv(filepath)
4767
+ async terminals(filepath, options = {}) {
4768
+ const includeVenvs = options.includeVenvs !== false
4769
+ let venvs = []
4770
+ if (includeVenvs) {
4771
+ venvs = await Util.find_venv(filepath)
4772
+ }
4770
4773
  let terminal
4771
4774
  const windowsBashPath = await this.resolveWindowsBashPath()
4772
4775
  const hasWindowsBashOption = this.kernel.platform === "win32" && typeof windowsBashPath === "string" && windowsBashPath.length > 0
@@ -10766,7 +10769,7 @@ class Server {
10766
10769
  }))
10767
10770
  this.app.get("/d/*", ex(async (req, res) => {
10768
10771
  let filepath = Util.u2p(req.params[0])
10769
- let terminal = await this.terminals(filepath)
10772
+ let terminal = await this.terminals(filepath, { includeVenvs: false })
10770
10773
  let plugin = await this.getPluginGlobal(req, this.kernel.plugin.config, terminal, filepath)
10771
10774
  let html = ""
10772
10775
  let plugin_menu
@@ -10909,6 +10912,7 @@ class Server {
10909
10912
  install: this.install,
10910
10913
  agent: req.agent,
10911
10914
  theme: this.theme,
10915
+ terminals_url: "/pinokio/d-terminals/" + req.params[0],
10912
10916
  //dynamic: plugin_menu
10913
10917
  dynamic,
10914
10918
  })
@@ -11051,6 +11055,23 @@ class Server {
11051
11055
  res.send("")
11052
11056
  }
11053
11057
  }))
11058
+ this.app.get("/pinokio/d-terminals/*", ex(async (req, res) => {
11059
+ let filepath = Util.u2p(req.params[0])
11060
+ let terminal = await this.terminals(filepath)
11061
+ const html = await new Promise((resolve, reject) => {
11062
+ ejs.renderFile(path.resolve(__dirname, "views/partials/d_terminal_column.ejs"), {
11063
+ userTerminal: terminal,
11064
+ terminals_url: null,
11065
+ }, (err, html) => {
11066
+ if (err) {
11067
+ reject(err)
11068
+ return
11069
+ }
11070
+ resolve(html)
11071
+ })
11072
+ })
11073
+ res.send(html)
11074
+ }))
11054
11075
  this.app.get("/pinokio/dynamic/:name", ex(async (req, res) => {
11055
11076
  // await this.kernel.plugin.init()
11056
11077
 
@@ -3928,9 +3928,6 @@ document.addEventListener("DOMContentLoaded", () => {
3928
3928
  }
3929
3929
  const fallbackUrl = buildAskAiLaunchUrl(tool.href, workspaceCwd);
3930
3930
  if (fallbackUrl) {
3931
- refreshTerminalSessions(fallbackUrl, workspaceCwd, {
3932
- retryDelays: []
3933
- });
3934
3931
  window.location.href = fallbackUrl;
3935
3932
  }
3936
3933
  }
@@ -5302,6 +5302,7 @@ header.navheader .mode-selector .community-mode-toggle {
5302
5302
  let interacted = false
5303
5303
  let global_selector
5304
5304
  let pendingSelectionRetry = null
5305
+ let initialWorkspaceDiskUsageRequested = false
5305
5306
  const scheduleSelectionRetry = (delay = 100) => {
5306
5307
  if (pendingSelectionRetry !== null) {
5307
5308
  return
@@ -5470,10 +5471,12 @@ header.navheader .mode-selector .community-mode-toggle {
5470
5471
  })
5471
5472
  let frame = document.createElement("iframe")
5472
5473
  frame.name = name
5473
- refreshTerminalSessions(href)
5474
5474
  frame.src = href
5475
5475
  frame.classList.add("selected")
5476
5476
  iframe_onerror(frame)
5477
+ frame.addEventListener("load", () => {
5478
+ requestInitialWorkspaceDiskUsage()
5479
+ }, { once: true })
5477
5480
  frame.setAttribute(
5478
5481
  "allow",
5479
5482
  "clipboard-read *; clipboard-write *; accelerometer *; ambient-light-sensor *; autoplay *; battery *; camera *; display-capture *; fullscreen *; gamepad *; geolocation *; gyroscope *; hid *; identity-credentials-get *; microphone *; midi *; otp-credentials *; serial *;"
@@ -5484,17 +5487,6 @@ header.navheader .mode-selector .community-mode-toggle {
5484
5487
  document.querySelector("main").appendChild(frame)
5485
5488
  loaded[name] = true
5486
5489
  }
5487
- function refreshTerminalSessions(rawUrl, workspaceCwd = "", options = {}) {
5488
- try {
5489
- const discovery = window.PinokioTerminalsDiscovery
5490
- if (!discovery || typeof discovery.refreshTerminalSessions !== "function") {
5491
- return false
5492
- }
5493
- return discovery.refreshTerminalSessions(rawUrl, workspaceCwd, options)
5494
- } catch (_) {
5495
- return false
5496
- }
5497
- }
5498
5490
  installBrowserviewInjectTargetObserver()
5499
5491
  document.addEventListener("click", (e) => {
5500
5492
  interacted = true
@@ -6579,14 +6571,10 @@ const rerenderMenuSection = (container, html) => {
6579
6571
 
6580
6572
 
6581
6573
  // Instantiate a frame with the selected target's href
6582
- let url = target.href
6583
6574
  const targetInjectDescriptors = readLinkInjectDescriptors(target)
6584
- if (!url) {
6575
+ if (!target.href) {
6585
6576
  return
6586
6577
  }
6587
- // document.querySelector("#open-browser").href = url
6588
- // document.querySelector("#clone-tab").setAttribute("data-href", url)
6589
-
6590
6578
  //document.querySelector("#location").value = url
6591
6579
 
6592
6580
  // document.querySelector("#open-location").setAttribute('href', target.href)
@@ -6634,6 +6622,7 @@ const rerenderMenuSection = (container, html) => {
6634
6622
  eventParam.preventDefault()
6635
6623
  eventParam.stopPropagation()
6636
6624
  }
6625
+ requestInitialWorkspaceDiskUsage()
6637
6626
  browserPopoutSurface.show(target)
6638
6627
  return
6639
6628
  }
@@ -6709,7 +6698,6 @@ const rerenderMenuSection = (container, html) => {
6709
6698
  let mode = target.getAttribute("data-mode")
6710
6699
  if (mode === "refresh") {
6711
6700
  if (targetFrame.src !== target.href) {
6712
- refreshTerminalSessions(target.href)
6713
6701
  targetFrame.src = target.href
6714
6702
  }
6715
6703
  } else {
@@ -6720,7 +6708,6 @@ const rerenderMenuSection = (container, html) => {
6720
6708
  let sub = subUrlOf(target.href, targetFrame.src)
6721
6709
  if (!sub) {
6722
6710
  if (targetFrame.src !== target.href) {
6723
- refreshTerminalSessions(target.href)
6724
6711
  targetFrame.src = target.href
6725
6712
  }
6726
6713
  }
@@ -6736,7 +6723,6 @@ const rerenderMenuSection = (container, html) => {
6736
6723
  } else {
6737
6724
  if (force) {
6738
6725
  if (targetFrame.src !== target.href) {
6739
- refreshTerminalSessions(target.href)
6740
6726
  targetFrame.src = target.href
6741
6727
  }
6742
6728
  }
@@ -6758,15 +6744,17 @@ const rerenderMenuSection = (container, html) => {
6758
6744
  } else {
6759
6745
  let frame = document.createElement("iframe")
6760
6746
  frame.name = target.target
6761
- refreshTerminalSessions(target.href)
6762
6747
  frame.src = target.href
6763
- iframe_onerror(frame)
6764
- frame.setAttribute(
6765
- "allow",
6766
- "clipboard-read *; clipboard-write *; accelerometer *; ambient-light-sensor *; autoplay *; battery *; camera *; display-capture *; fullscreen *; gamepad *; geolocation *; gyroscope *; hid *; identity-credentials-get *; microphone *; midi *; otp-credentials *; serial *;"
6748
+ iframe_onerror(frame)
6749
+ frame.addEventListener("load", () => {
6750
+ requestInitialWorkspaceDiskUsage()
6751
+ }, { once: true })
6752
+ frame.setAttribute(
6753
+ "allow",
6754
+ "clipboard-read *; clipboard-write *; accelerometer *; ambient-light-sensor *; autoplay *; battery *; camera *; display-capture *; fullscreen *; gamepad *; geolocation *; gyroscope *; hid *; identity-credentials-get *; microphone *; midi *; otp-credentials *; serial *;"
6767
6755
  )
6768
6756
  frame.setAttribute("allowfullscreen", "")
6769
- writeFrameInjectDescriptors(frame, targetInjectDescriptors)
6757
+ writeFrameInjectDescriptors(frame, targetInjectDescriptors)
6770
6758
  publishBrowserviewInjectTargets([frame], { sync: true })
6771
6759
  document.querySelector("main").appendChild(frame)
6772
6760
  loaded[target.target] = true
@@ -6922,14 +6910,17 @@ const rerenderMenuSection = (container, html) => {
6922
6910
 
6923
6911
  document.querySelector(".temp-menu").appendChild(item)
6924
6912
 
6925
- let frame = document.createElement("iframe")
6926
- frame.name = id
6927
- frame.src = item.href
6928
- iframe_onerror(frame)
6929
- frame.setAttribute(
6930
- "allow",
6931
- "clipboard-read *; clipboard-write *; accelerometer *; ambient-light-sensor *; autoplay *; battery *; camera *; display-capture *; fullscreen *; gamepad *; geolocation *; gyroscope *; hid *; identity-credentials-get *; microphone *; midi *; otp-credentials *; serial *;"
6932
- )
6913
+ let frame = document.createElement("iframe")
6914
+ frame.name = id
6915
+ frame.src = item.href
6916
+ iframe_onerror(frame)
6917
+ frame.addEventListener("load", () => {
6918
+ requestInitialWorkspaceDiskUsage()
6919
+ }, { once: true })
6920
+ frame.setAttribute(
6921
+ "allow",
6922
+ "clipboard-read *; clipboard-write *; accelerometer *; ambient-light-sensor *; autoplay *; battery *; camera *; display-capture *; fullscreen *; gamepad *; geolocation *; gyroscope *; hid *; identity-credentials-get *; microphone *; midi *; otp-credentials *; serial *;"
6923
+ )
6933
6924
  frame.setAttribute("allowfullscreen", "")
6934
6925
  document.querySelector("main").appendChild(frame)
6935
6926
 
@@ -7998,6 +7989,14 @@ const rerenderMenuSection = (container, html) => {
7998
7989
  })
7999
7990
  }
8000
7991
  }
7992
+ const requestInitialWorkspaceDiskUsage = () => {
7993
+ if (initialWorkspaceDiskUsageRequested) {
7994
+ return
7995
+ }
7996
+ initialWorkspaceDiskUsageRequested = true
7997
+ refresh_du()
7998
+ refresh_du("logs")
7999
+ }
8001
8000
 
8002
8001
  const try_dynamic = async (options = {}) => {
8003
8002
  let rendered
@@ -8032,7 +8031,8 @@ const rerenderMenuSection = (container, html) => {
8032
8031
  } else {
8033
8032
  rendered = false
8034
8033
  }
8035
- if (!rendered) {
8034
+ if (rendered) {
8035
+ } else {
8036
8036
  if (options.retryOnEmpty === false) {
8037
8037
  return
8038
8038
  }
@@ -8089,13 +8089,9 @@ const rerenderMenuSection = (container, html) => {
8089
8089
  renderSelection()
8090
8090
  }
8091
8091
  }
8092
- let init = document.querySelector("[data-init]")
8093
- if (init) {
8094
- init.click()
8095
- }
8096
8092
  <% if (type === 'browse') { %>
8097
8093
  setTimeout(() => {
8098
- try_dynamic({ retryOnEmpty: false })
8094
+ try_dynamic()
8099
8095
  }, 0)
8100
8096
  <% } %>
8101
8097
  window.addEventListener('message', (event) => {
@@ -8155,6 +8151,8 @@ const rerenderMenuSection = (container, html) => {
8155
8151
  if (event.data.type === 'stream') {
8156
8152
  const frameName = resolveFrameName(null, event.source)
8157
8153
  updateTabTimestamp(frameName, Date.now())
8154
+ } else if (event.data.type === 'idle') {
8155
+ return
8158
8156
  } else if (event.data.type === 'restart') {
8159
8157
  <% if (type === 'run') { %>
8160
8158
  clearPersistedFrameLinkSelection()
@@ -8183,8 +8181,6 @@ const rerenderMenuSection = (container, html) => {
8183
8181
 
8184
8182
 
8185
8183
  });
8186
- refresh_du()
8187
- refresh_du("logs")
8188
8184
  renderSelection({ force: true })
8189
8185
  <% if (type === "browse" || type === "files") { %>
8190
8186
  const repoStatusCache = new Map()
@@ -12405,7 +12401,6 @@ document.addEventListener("DOMContentLoaded", () => {
12405
12401
  if (!normalized) {
12406
12402
  return false
12407
12403
  }
12408
- refreshTerminalSessions(normalized, workspaceCwd)
12409
12404
  pickerRequestId += 1
12410
12405
  currentUrl = normalized
12411
12406
  setLocation(normalized)
@@ -524,51 +524,7 @@ body.dark #update-spec {
524
524
 
525
525
  <div class='menu-grid <%= primaryColumnCount === 3 ? "menu-grid--triptych" : "" %>'>
526
526
  <% if (userTerminal && userTerminal.menu && userTerminal.menu.length) { %>
527
- <div class='menu-container menu-column user-terminal'>
528
- <div class='tab-header'>
529
- <h3><i class='<%= userTerminal.icon %>'></i> <%= userTerminal.title %></h3>
530
- </div>
531
- <% if (userTerminal.subtitle || userTerminal.subtitle_link_href) { %>
532
- <div class='column-subtitle'>
533
- <% if (userTerminal.subtitle) { %>
534
- <span><%= userTerminal.subtitle %></span><% if (userTerminal.subtitle_link_href && userTerminal.subtitle_link_label) { %> <% } %>
535
- <% } %>
536
- <% if (userTerminal.subtitle_link_href && userTerminal.subtitle_link_label) { %>
537
- <a class='column-subtitle-link' href="<%= userTerminal.subtitle_link_href %>" target="_parent"><%= userTerminal.subtitle_link_label %></a>
538
- <% } %>
539
- </div>
540
- <% } %>
541
- <div class='tab-content'>
542
- <% userTerminal.menu.forEach((i) => { %>
543
- <div class='tab' role="button" tabindex="0" data-index="<%= index++ %>" data-target="@<%= i.href %>" data-href="<%= i.href %>">
544
- <% if (i.image) { %>
545
- <img src="<%= i.image %>">
546
- <% } else if (i.icon) { %>
547
- <i class="img <%= i.icon %>"></i>
548
- <% } %>
549
- <div class='tab-copy'>
550
- <h2 class='tab-title'><%= i.title %></h2>
551
- <% if (i.subtitle) { %>
552
- <div class='tab-subtitle subtitle'><%= i.subtitle %></div>
553
- <% } %>
554
- </div>
555
- <div class='tab-actions'>
556
- <% if (i.link) { %>
557
- <button class="tab-action-link" type="button" data-doc-link="true" data-href="<%= i.link %>" title="Open docs" data-tippy-content="Open docs" aria-label="Open docs">
558
- <i class="fa-solid fa-circle-info"></i>
559
- </button>
560
- <% } %>
561
- <button class="ai-perm-link" type="button" data-ai-consent="<%= i.href %>" title="AI permissions" data-tippy-content="AI permissions" aria-label="AI permissions">
562
- <i class="fa-solid fa-shield-halved"></i>
563
- </button>
564
- <div class='disclosure-indicator' aria-hidden="true">
565
- <i class="fa-solid fa-chevron-right"></i>
566
- </div>
567
- </div>
568
- </div>
569
- <% }) %>
570
- </div>
571
- </div>
527
+ <%- include('./partials/d_terminal_column', { userTerminal, terminals_url }) %>
572
528
  <% } %>
573
529
 
574
530
  <% if (cliMenu && cliMenu.menu && cliMenu.menu.length) { %>
@@ -797,12 +753,16 @@ document.querySelector("#update-spec").addEventListener("click", (e) => {
797
753
  })
798
754
  */
799
755
  let list = []
800
- document.querySelectorAll(".tab").forEach((el, index) => {
801
- list.push({
802
- index: parseInt(el.getAttribute("data-index")),
803
- text: el.textContent,
756
+ const rebuildTabList = () => {
757
+ list = []
758
+ document.querySelectorAll(".tab").forEach((el, index) => {
759
+ el.setAttribute("data-index", String(index))
760
+ list.push({
761
+ index,
762
+ text: el.textContent,
763
+ })
804
764
  })
805
- })
765
+ }
806
766
  const search = (items, value) => {
807
767
  let filtered = []
808
768
  for(let i=0; i<items.length; i++) {
@@ -848,6 +808,7 @@ const renderSearch = () => {
848
808
  }
849
809
  }
850
810
  }
811
+ rebuildTabList()
851
812
  renderSearch()
852
813
  const workspaceCwd = (() => {
853
814
  const node = document.querySelector(".file-open")
@@ -973,6 +934,34 @@ document.querySelector("form input[type=search]").addEventListener("input", (e)
973
934
  renderSearch()
974
935
  })
975
936
  document.querySelector("form input[type=search]").focus()
937
+ const hydrateTerminalColumn = async () => {
938
+ const terminalColumn = document.querySelector("[data-dev-terminals-column][data-terminals-url]")
939
+ if (!terminalColumn) {
940
+ return
941
+ }
942
+ const url = terminalColumn.getAttribute("data-terminals-url")
943
+ if (!url) {
944
+ return
945
+ }
946
+ try {
947
+ const html = await fetch(url).then((res) => res.text())
948
+ if (!html || html.trim().length === 0) {
949
+ return
950
+ }
951
+ const wrapper = document.createElement("div")
952
+ wrapper.innerHTML = html
953
+ const nextColumn = wrapper.firstElementChild
954
+ if (!nextColumn) {
955
+ return
956
+ }
957
+ terminalColumn.replaceWith(nextColumn)
958
+ rebuildTabList()
959
+ renderSearch()
960
+ } catch (_) {}
961
+ }
962
+ setTimeout(() => {
963
+ hydrateTerminalColumn()
964
+ }, 0)
976
965
  document.addEventListener("click", (e) => {
977
966
  const permTarget = e.target.closest('[data-ai-consent]')
978
967
  if (permTarget) {
@@ -0,0 +1,46 @@
1
+ <div class='menu-container menu-column user-terminal' data-dev-terminals-column="true"<% if (terminals_url) { %> data-terminals-url="<%= terminals_url %>"<% } %>>
2
+ <div class='tab-header'>
3
+ <h3><i class='<%= userTerminal.icon %>'></i> <%= userTerminal.title %></h3>
4
+ </div>
5
+ <% if (userTerminal.subtitle || userTerminal.subtitle_link_href) { %>
6
+ <div class='column-subtitle'>
7
+ <% if (userTerminal.subtitle) { %>
8
+ <span><%= userTerminal.subtitle %></span><% if (userTerminal.subtitle_link_href && userTerminal.subtitle_link_label) { %> <% } %>
9
+ <% } %>
10
+ <% if (userTerminal.subtitle_link_href && userTerminal.subtitle_link_label) { %>
11
+ <a class='column-subtitle-link' href="<%= userTerminal.subtitle_link_href %>" target="_parent"><%= userTerminal.subtitle_link_label %></a>
12
+ <% } %>
13
+ </div>
14
+ <% } %>
15
+ <div class='tab-content'>
16
+ <% let localIndex = 0 %>
17
+ <% userTerminal.menu.forEach((i) => { %>
18
+ <div class='tab' role="button" tabindex="0" data-index="<%= localIndex++ %>" data-target="@<%= i.href %>" data-href="<%= i.href %>">
19
+ <% if (i.image) { %>
20
+ <img src="<%= i.image %>">
21
+ <% } else if (i.icon) { %>
22
+ <i class="img <%= i.icon %>"></i>
23
+ <% } %>
24
+ <div class='tab-copy'>
25
+ <h2 class='tab-title'><%= i.title %></h2>
26
+ <% if (i.subtitle) { %>
27
+ <div class='tab-subtitle subtitle'><%= i.subtitle %></div>
28
+ <% } %>
29
+ </div>
30
+ <div class='tab-actions'>
31
+ <% if (i.link) { %>
32
+ <button class="tab-action-link" type="button" data-doc-link="true" data-href="<%= i.link %>" title="Open docs" data-tippy-content="Open docs" aria-label="Open docs">
33
+ <i class="fa-solid fa-circle-info"></i>
34
+ </button>
35
+ <% } %>
36
+ <button class="ai-perm-link" type="button" data-ai-consent="<%= i.href %>" title="AI permissions" data-tippy-content="AI permissions" aria-label="AI permissions">
37
+ <i class="fa-solid fa-shield-halved"></i>
38
+ </button>
39
+ <div class='disclosure-indicator' aria-hidden="true">
40
+ <i class="fa-solid fa-chevron-right"></i>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ <% }) %>
45
+ </div>
46
+ </div>
@@ -552,6 +552,67 @@ const createRunControls = () => {
552
552
  }
553
553
  return { set }
554
554
  }
555
+ const PLUGIN_TERMINAL_IDLE_WINDOW_MS = 1200
556
+ const createPluginTerminalDiscoveryRefresher = (context = {}) => {
557
+ const enabled = (() => {
558
+ try {
559
+ return window.location.pathname.startsWith("/run/plugin/")
560
+ } catch (_) {
561
+ return false
562
+ }
563
+ })()
564
+ let idleTimer = null
565
+ let armed = false
566
+ let sawStream = false
567
+ const clearIdleTimer = () => {
568
+ if (idleTimer) {
569
+ clearTimeout(idleTimer)
570
+ idleTimer = null
571
+ }
572
+ }
573
+ const refresh = () => {
574
+ if (!enabled || !armed || !sawStream) {
575
+ return
576
+ }
577
+ armed = false
578
+ sawStream = false
579
+ try {
580
+ const discovery = window.PinokioTerminalsDiscovery
581
+ if (!discovery || typeof discovery.refreshTerminalSessions !== "function") {
582
+ return
583
+ }
584
+ discovery.refreshTerminalSessions(window.location.href, context.cwd || "", {
585
+ retryDelays: []
586
+ })
587
+ } catch (_) {}
588
+ }
589
+ return {
590
+ arm(meaningful) {
591
+ if (!enabled || !meaningful) {
592
+ return
593
+ }
594
+ armed = true
595
+ sawStream = false
596
+ clearIdleTimer()
597
+ },
598
+ markActivity() {
599
+ if (!enabled || !armed) {
600
+ return
601
+ }
602
+ sawStream = true
603
+ clearIdleTimer()
604
+ idleTimer = setTimeout(() => {
605
+ idleTimer = null
606
+ refresh()
607
+ }, PLUGIN_TERMINAL_IDLE_WINDOW_MS)
608
+ },
609
+ clear() {
610
+ armed = false
611
+ sawStream = false
612
+ clearIdleTimer()
613
+ }
614
+ }
615
+ }
555
616
  const createAiConsentManager = (context = {}) => {
556
617
  const storage = getSafeLocalStorage()
557
618
  const storageSupported = !!storage
@@ -848,6 +909,9 @@ document.addEventListener("DOMContentLoaded", async () => {
848
909
  uri: scriptUri || ("~" + location.pathname)
849
910
  })
850
911
  const runControls = createRunControls()
912
+ const pluginTerminalDiscoveryRefresher = createPluginTerminalDiscoveryRefresher({
913
+ cwd: scriptCwd
914
+ })
851
915
  consentManager.showChipIfRemembered()
852
916
  class RPC {
853
917
  constructor() {
@@ -923,16 +987,17 @@ document.addEventListener("DOMContentLoaded", async () => {
923
987
  }
924
988
  }
925
989
  notifyLineSubmitted(line, meta = {}) {
926
- if (this.inputTracker) {
927
- this.inputTracker.submit(line, meta)
928
- return
929
- }
930
990
  const safeLine = sanitizePreviewLine(line || "")
931
991
  const preview = safeLine.trim()
932
992
  const limit = 200
933
993
  const truncated = preview.length > limit ? preview.slice(0, limit) + "..." : preview
934
994
  const hadLineBreak = Boolean(meta && meta.hadLineBreak)
935
995
  const meaningful = truncated.length > 0 || hadLineBreak
996
+ pluginTerminalDiscoveryRefresher.arm(meaningful)
997
+ if (this.inputTracker) {
998
+ this.inputTracker.submit(line, meta)
999
+ return
1000
+ }
936
1001
  postMessageToAncestors({
937
1002
  type: "terminal-input",
938
1003
  frame: window.name || null,
@@ -1096,6 +1161,7 @@ document.addEventListener("DOMContentLoaded", async () => {
1096
1161
  }
1097
1162
  } else if (packet.type === "stream") {
1098
1163
  refreshParent(packet)
1164
+ pluginTerminalDiscoveryRefresher.markActivity()
1099
1165
  // set the current shell id
1100
1166
  const previousShellId = shell_id
1101
1167
  if (packet.data.id) {
@@ -1128,6 +1194,7 @@ document.addEventListener("DOMContentLoaded", async () => {
1128
1194
  runControls.set("running")
1129
1195
  } else if (packet.type === 'disconnect') {
1130
1196
  refreshParent(packet)
1197
+ pluginTerminalDiscoveryRefresher.clear()
1131
1198
  reloadMemory()
1132
1199
  this.term.write("\r\nDisconnected...\r\n")
1133
1200
  document.querySelector("#status-window").innerHTML = "<b>Ready</b>"
@@ -1423,6 +1490,7 @@ document.addEventListener("DOMContentLoaded", async () => {
1423
1490
  n.Noty(payload)
1424
1491
  }
1425
1492
  } else if (packet.type === "restart") {
1493
+ pluginTerminalDiscoveryRefresher.clear()
1426
1494
  try {
1427
1495
  if (window.parent && window.parent !== window && typeof window.parent.postMessage === "function") {
1428
1496
  window.parent.postMessage(packet, "*")
@@ -1469,6 +1537,7 @@ document.addEventListener("DOMContentLoaded", async () => {
1469
1537
  text: `${packet.data}`,
1470
1538
  })
1471
1539
  } else if (packet.type === "error") {
1540
+ pluginTerminalDiscoveryRefresher.clear()
1472
1541
  runControls.set("idle")
1473
1542
 
1474
1543
 
@@ -1547,6 +1616,7 @@ document.addEventListener("DOMContentLoaded", async () => {
1547
1616
  type: 'success'
1548
1617
  })
1549
1618
  */
1619
+ pluginTerminalDiscoveryRefresher.clear()
1550
1620
  runControls.set("idle")
1551
1621
  }, 0)
1552
1622
  //this.socket.close()
@@ -1575,6 +1645,7 @@ document.addEventListener("DOMContentLoaded", async () => {
1575
1645
  this.mode = (mode ? mode : "run")
1576
1646
  const allowed = aiConsentRequired ? await consentManager.ensureAllowed() : true
1577
1647
  if (!allowed) {
1648
+ pluginTerminalDiscoveryRefresher.clear()
1578
1649
  runControls.set("idle")
1579
1650
  n.Noty({
1580
1651
  text: "Run canceled; AI agents remain blocked for this folder.",
@@ -1601,6 +1672,7 @@ document.addEventListener("DOMContentLoaded", async () => {
1601
1672
  await this.start(mode)
1602
1673
  return true
1603
1674
  } catch (error) {
1675
+ pluginTerminalDiscoveryRefresher.clear()
1604
1676
  runControls.set("idle")
1605
1677
  const message = error && error.message ? error.message : "Failed to start"
1606
1678
  n.Noty({