Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
M
macos-hub-skills
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
郭阔
macos-hub-skills
Commits
95cd2d21
Commit
95cd2d21
authored
Aug 13, 2026
by
谢宇轩
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: edit chromium tab bug
parent
4685f05e
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
28 additions
and
8 deletions
+28
-8
ensure-chromium.js
skills/news-harvester/scripts/ensure-chromium.js
+14
-4
ensure-chromium.js
skills/news-source-analyzer/scripts/ensure-chromium.js
+14
-4
No files found.
skills/news-harvester/scripts/ensure-chromium.js
View file @
95cd2d21
...
@@ -269,9 +269,16 @@ function looksLikeDisplayFailure(log) {
...
@@ -269,9 +269,16 @@ function looksLikeDisplayFailure(log) {
// startup tab is an orphan that wastes a tab slot and inflates Target.getTargets
// startup tab is an orphan that wastes a tab slot and inflates Target.getTargets
// counts. Close it via the HTTP /json/close/<id> endpoint (no WebSocket needed).
// counts. Close it via the HTTP /json/close/<id> endpoint (no WebSocket needed).
// Only closes about:blank / chrome://newtab — never touches real harvest tabs.
// Only closes about:blank / chrome://newtab — never touches real harvest tabs.
//
// IMPORTANT (windowed mode / headless=false): Chromium quits when its last tab
// is closed — this is standard browser behavior, not a bug. In headless mode
// the browser stays alive with zero tabs, but in windowed mode it does not.
// When headless=false, we must keep at least one tab open as a "keep-alive" tab
// so that harvest sessions closing their own tabs don't accidentally kill the
// browser. We keep the FIRST startup tab and only close the rest (if any).
const
STARTUP_TAB_URLS
=
new
Set
([
'about:blank'
,
'chrome://newtab/'
,
'chrome://newtab'
]);
const
STARTUP_TAB_URLS
=
new
Set
([
'about:blank'
,
'chrome://newtab/'
,
'chrome://newtab'
]);
function
closeStartupTabs
(
port
)
{
function
closeStartupTabs
(
port
,
keepAlive
=
false
)
{
return
new
Promise
((
resolve
)
=>
{
return
new
Promise
((
resolve
)
=>
{
// agent: false → don't pool sockets in the global HTTP agent. Without this
// agent: false → don't pool sockets in the global HTTP agent. Without this
// the keep-alive sockets linger and keep the Node event loop alive, hanging
// the keep-alive sockets linger and keep the Node event loop alive, hanging
...
@@ -282,7 +289,10 @@ function closeStartupTabs(port) {
...
@@ -282,7 +289,10 @@ function closeStartupTabs(port) {
res
.
on
(
'end'
,
()
=>
{
res
.
on
(
'end'
,
()
=>
{
try
{
try
{
const
tabs
=
JSON
.
parse
(
data
);
const
tabs
=
JSON
.
parse
(
data
);
const
orphans
=
tabs
.
filter
(
t
=>
t
.
type
===
'page'
&&
STARTUP_TAB_URLS
.
has
(
t
.
url
));
let
orphans
=
tabs
.
filter
(
t
=>
t
.
type
===
'page'
&&
STARTUP_TAB_URLS
.
has
(
t
.
url
));
// In windowed mode, keep the first startup tab alive so the browser
// doesn't quit when harvest tabs close. Only close extras.
if
(
keepAlive
&&
orphans
.
length
>
0
)
orphans
=
orphans
.
slice
(
1
);
if
(
orphans
.
length
===
0
)
return
resolve
(
0
);
if
(
orphans
.
length
===
0
)
return
resolve
(
0
);
let
remaining
=
orphans
.
length
;
let
remaining
=
orphans
.
length
;
for
(
const
tab
of
orphans
)
{
for
(
const
tab
of
orphans
)
{
...
@@ -306,7 +316,7 @@ async function ensureChromium(config, portOverride) {
...
@@ -306,7 +316,7 @@ async function ensureChromium(config, portOverride) {
// 1. Already up? (idempotent — safe to call every run)
// 1. Already up? (idempotent — safe to call every run)
try
{
try
{
const
v
=
await
cdpVersion
(
opts
.
cdpPort
,
1500
);
const
v
=
await
cdpVersion
(
opts
.
cdpPort
,
1500
);
await
closeStartupTabs
(
opts
.
cdpPort
);
// clean orphan tabs from prior runs
await
closeStartupTabs
(
opts
.
cdpPort
,
!
opts
.
headless
);
// keep-alive tab in windowed mode
return
{
port
:
opts
.
cdpPort
,
alreadyRunning
:
true
,
browser
:
v
.
Browser
};
return
{
port
:
opts
.
cdpPort
,
alreadyRunning
:
true
,
browser
:
v
.
Browser
};
}
catch
(
e
)
{
/* not up yet, fall through to launch */
}
}
catch
(
e
)
{
/* not up yet, fall through to launch */
}
...
@@ -323,7 +333,7 @@ async function ensureChromium(config, portOverride) {
...
@@ -323,7 +333,7 @@ async function ensureChromium(config, portOverride) {
// 4. Wait for readiness
// 4. Wait for readiness
try
{
try
{
const
v
=
await
waitForCdp
(
opts
.
cdpPort
,
30000
,
500
);
const
v
=
await
waitForCdp
(
opts
.
cdpPort
,
30000
,
500
);
await
closeStartupTabs
(
opts
.
cdpPort
);
// close the initial about:blank tab
await
closeStartupTabs
(
opts
.
cdpPort
,
!
opts
.
headless
);
// keep-alive tab in windowed mode
return
{
port
:
opts
.
cdpPort
,
pid
,
browser
:
v
.
Browser
,
bin
,
logPath
};
return
{
port
:
opts
.
cdpPort
,
pid
,
browser
:
v
.
Browser
,
bin
,
logPath
};
}
catch
(
e
)
{
}
catch
(
e
)
{
const
logTail
=
tailFile
(
logPath
);
const
logTail
=
tailFile
(
logPath
);
...
...
skills/news-source-analyzer/scripts/ensure-chromium.js
View file @
95cd2d21
...
@@ -269,9 +269,16 @@ function looksLikeDisplayFailure(log) {
...
@@ -269,9 +269,16 @@ function looksLikeDisplayFailure(log) {
// startup tab is an orphan that wastes a tab slot and inflates Target.getTargets
// startup tab is an orphan that wastes a tab slot and inflates Target.getTargets
// counts. Close it via the HTTP /json/close/<id> endpoint (no WebSocket needed).
// counts. Close it via the HTTP /json/close/<id> endpoint (no WebSocket needed).
// Only closes about:blank / chrome://newtab — never touches real harvest tabs.
// Only closes about:blank / chrome://newtab — never touches real harvest tabs.
//
// IMPORTANT (windowed mode / headless=false): Chromium quits when its last tab
// is closed — this is standard browser behavior, not a bug. In headless mode
// the browser stays alive with zero tabs, but in windowed mode it does not.
// When headless=false, we must keep at least one tab open as a "keep-alive" tab
// so that harvest sessions closing their own tabs don't accidentally kill the
// browser. We keep the FIRST startup tab and only close the rest (if any).
const
STARTUP_TAB_URLS
=
new
Set
([
'about:blank'
,
'chrome://newtab/'
,
'chrome://newtab'
]);
const
STARTUP_TAB_URLS
=
new
Set
([
'about:blank'
,
'chrome://newtab/'
,
'chrome://newtab'
]);
function
closeStartupTabs
(
port
)
{
function
closeStartupTabs
(
port
,
keepAlive
=
false
)
{
return
new
Promise
((
resolve
)
=>
{
return
new
Promise
((
resolve
)
=>
{
// agent: false → don't pool sockets in the global HTTP agent. Without this
// agent: false → don't pool sockets in the global HTTP agent. Without this
// the keep-alive sockets linger and keep the Node event loop alive, hanging
// the keep-alive sockets linger and keep the Node event loop alive, hanging
...
@@ -282,7 +289,10 @@ function closeStartupTabs(port) {
...
@@ -282,7 +289,10 @@ function closeStartupTabs(port) {
res
.
on
(
'end'
,
()
=>
{
res
.
on
(
'end'
,
()
=>
{
try
{
try
{
const
tabs
=
JSON
.
parse
(
data
);
const
tabs
=
JSON
.
parse
(
data
);
const
orphans
=
tabs
.
filter
(
t
=>
t
.
type
===
'page'
&&
STARTUP_TAB_URLS
.
has
(
t
.
url
));
let
orphans
=
tabs
.
filter
(
t
=>
t
.
type
===
'page'
&&
STARTUP_TAB_URLS
.
has
(
t
.
url
));
// In windowed mode, keep the first startup tab alive so the browser
// doesn't quit when harvest tabs close. Only close extras.
if
(
keepAlive
&&
orphans
.
length
>
0
)
orphans
=
orphans
.
slice
(
1
);
if
(
orphans
.
length
===
0
)
return
resolve
(
0
);
if
(
orphans
.
length
===
0
)
return
resolve
(
0
);
let
remaining
=
orphans
.
length
;
let
remaining
=
orphans
.
length
;
for
(
const
tab
of
orphans
)
{
for
(
const
tab
of
orphans
)
{
...
@@ -306,7 +316,7 @@ async function ensureChromium(config, portOverride) {
...
@@ -306,7 +316,7 @@ async function ensureChromium(config, portOverride) {
// 1. Already up? (idempotent — safe to call every run)
// 1. Already up? (idempotent — safe to call every run)
try
{
try
{
const
v
=
await
cdpVersion
(
opts
.
cdpPort
,
1500
);
const
v
=
await
cdpVersion
(
opts
.
cdpPort
,
1500
);
await
closeStartupTabs
(
opts
.
cdpPort
);
// clean orphan tabs from prior runs
await
closeStartupTabs
(
opts
.
cdpPort
,
!
opts
.
headless
);
// keep-alive tab in windowed mode
return
{
port
:
opts
.
cdpPort
,
alreadyRunning
:
true
,
browser
:
v
.
Browser
};
return
{
port
:
opts
.
cdpPort
,
alreadyRunning
:
true
,
browser
:
v
.
Browser
};
}
catch
(
e
)
{
/* not up yet, fall through to launch */
}
}
catch
(
e
)
{
/* not up yet, fall through to launch */
}
...
@@ -323,7 +333,7 @@ async function ensureChromium(config, portOverride) {
...
@@ -323,7 +333,7 @@ async function ensureChromium(config, portOverride) {
// 4. Wait for readiness
// 4. Wait for readiness
try
{
try
{
const
v
=
await
waitForCdp
(
opts
.
cdpPort
,
30000
,
500
);
const
v
=
await
waitForCdp
(
opts
.
cdpPort
,
30000
,
500
);
await
closeStartupTabs
(
opts
.
cdpPort
);
// close the initial about:blank tab
await
closeStartupTabs
(
opts
.
cdpPort
,
!
opts
.
headless
);
// keep-alive tab in windowed mode
return
{
port
:
opts
.
cdpPort
,
pid
,
browser
:
v
.
Browser
,
bin
,
logPath
};
return
{
port
:
opts
.
cdpPort
,
pid
,
browser
:
v
.
Browser
,
bin
,
logPath
};
}
catch
(
e
)
{
}
catch
(
e
)
{
const
logTail
=
tailFile
(
logPath
);
const
logTail
=
tailFile
(
logPath
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment