@love-moon/ai-sdk 0.5.0 → 0.5.1
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/CHANGELOG.md +20 -0
- package/dist/worker.js +42 -11
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @love-moon/ai-sdk
|
|
2
2
|
|
|
3
|
+
## 0.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 39a49fc: fix: reclaim orphaned chat-web browser and cap chat-web task lifetime
|
|
8
|
+
|
|
9
|
+
chat-web persists one Chromium profile per provider, guarded by a per-profile
|
|
10
|
+
SingletonLock. A task whose browser was not cleaned up (e.g. the ai-sdk worker
|
|
11
|
+
was SIGKILLed) left an orphaned Chromium holding that lock, so the next task for
|
|
12
|
+
the same provider failed to launch with `Opening in existing browser session`.
|
|
13
|
+
|
|
14
|
+
- chat-web now reclaims stale/orphaned profile locks before launching (kills an
|
|
15
|
+
orphan whose owner process is gone, clears dead locks) and refuses with a
|
|
16
|
+
clear `ProfileLockedError` when a genuine live chat still holds the profile.
|
|
17
|
+
- The ai-sdk worker now closes its session (and browser) on SIGTERM/SIGINT and
|
|
18
|
+
bounds the close so it can't hang, preventing browser leaks on shutdown.
|
|
19
|
+
- conductor fire caps a chat-web task's active lifetime (default 24h,
|
|
20
|
+
`CONDUCTOR_CHATWEB_MAX_ACTIVE_MS`) and auto-stops it as
|
|
21
|
+
`KILLED / max_active_duration`; chat history is preserved.
|
|
22
|
+
|
|
3
23
|
## 0.5.0
|
|
4
24
|
|
|
5
25
|
## 0.4.2
|
package/dist/worker.js
CHANGED
|
@@ -96,7 +96,9 @@ async function handleRequest(message) {
|
|
|
96
96
|
result,
|
|
97
97
|
});
|
|
98
98
|
if (method === "close") {
|
|
99
|
-
|
|
99
|
+
// session.close() already ran via the dispatch above; route through the
|
|
100
|
+
// guarded shutdown so a racing signal can't double-handle the exit.
|
|
101
|
+
void gracefulShutdown(0);
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
104
|
async function dispatchMessage(message) {
|
|
@@ -117,38 +119,67 @@ async function dispatchMessage(message) {
|
|
|
117
119
|
});
|
|
118
120
|
}
|
|
119
121
|
}
|
|
122
|
+
// Upper bound on how long we wait for a graceful session close before
|
|
123
|
+
// giving up and exiting. A browser-backed session (chat-web) can hang in
|
|
124
|
+
// context.close(); without this cap the worker could fail to exit, which
|
|
125
|
+
// only makes orphaned browsers more likely. If close times out, the browser
|
|
126
|
+
// is reclaimed on the next launch via chat-web's profile-lock logic.
|
|
127
|
+
const CLOSE_TIMEOUT_MS = 10_000;
|
|
120
128
|
async function closeSession() {
|
|
121
129
|
if (!session || typeof session.close !== "function") {
|
|
122
130
|
return;
|
|
123
131
|
}
|
|
124
132
|
try {
|
|
125
|
-
await
|
|
133
|
+
await Promise.race([
|
|
134
|
+
Promise.resolve().then(() => session.close()),
|
|
135
|
+
new Promise((resolve) => {
|
|
136
|
+
const timer = setTimeout(resolve, CLOSE_TIMEOUT_MS);
|
|
137
|
+
if (typeof timer.unref === "function") {
|
|
138
|
+
timer.unref();
|
|
139
|
+
}
|
|
140
|
+
}),
|
|
141
|
+
]);
|
|
126
142
|
}
|
|
127
143
|
catch {
|
|
128
144
|
// best effort
|
|
129
145
|
}
|
|
130
146
|
}
|
|
131
|
-
|
|
147
|
+
// Graceful shutdown on termination signals. Without these handlers a worker
|
|
148
|
+
// killed via SIGTERM (e.g. when the parent fire is stopped) would exit
|
|
149
|
+
// without closing its session, leaking the Chromium browser it spawned.
|
|
150
|
+
let shuttingDown = false;
|
|
151
|
+
async function gracefulShutdown(exitCode) {
|
|
152
|
+
if (shuttingDown) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
shuttingDown = true;
|
|
156
|
+
await closeSession();
|
|
157
|
+
process.exit(exitCode);
|
|
158
|
+
}
|
|
159
|
+
process.on("SIGTERM", () => {
|
|
160
|
+
void gracefulShutdown(143);
|
|
161
|
+
});
|
|
162
|
+
process.on("SIGINT", () => {
|
|
163
|
+
void gracefulShutdown(130);
|
|
164
|
+
});
|
|
165
|
+
process.on("uncaughtException", (error) => {
|
|
132
166
|
send({
|
|
133
167
|
type: "event",
|
|
134
168
|
name: "worker_error",
|
|
135
169
|
payload: serializeError(error),
|
|
136
170
|
});
|
|
137
|
-
|
|
138
|
-
process.exit(1);
|
|
171
|
+
void gracefulShutdown(1);
|
|
139
172
|
});
|
|
140
|
-
process.on("unhandledRejection",
|
|
173
|
+
process.on("unhandledRejection", (reason) => {
|
|
141
174
|
send({
|
|
142
175
|
type: "event",
|
|
143
176
|
name: "worker_error",
|
|
144
177
|
payload: serializeError(reason),
|
|
145
178
|
});
|
|
146
|
-
|
|
147
|
-
process.exit(1);
|
|
179
|
+
void gracefulShutdown(1);
|
|
148
180
|
});
|
|
149
|
-
process.stdin.on("end",
|
|
150
|
-
|
|
151
|
-
process.exit(0);
|
|
181
|
+
process.stdin.on("end", () => {
|
|
182
|
+
void gracefulShutdown(0);
|
|
152
183
|
});
|
|
153
184
|
const input = readline.createInterface({ input: process.stdin });
|
|
154
185
|
let workQueue = Promise.resolve();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@love-moon/ai-sdk",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/lovemoon-ai/conductor.git"
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"zod": "^4.1.5"
|
|
33
33
|
},
|
|
34
34
|
"optionalDependencies": {
|
|
35
|
-
"@love-moon/chat-web": "^0.5.
|
|
35
|
+
"@love-moon/chat-web": "^0.5.1"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/node": "^22.10.2",
|
|
39
39
|
"tsx": "^4.20.6",
|
|
40
40
|
"typescript": "^5.6.3"
|
|
41
41
|
},
|
|
42
|
-
"gitCommitId": "
|
|
42
|
+
"gitCommitId": "119eab6"
|
|
43
43
|
}
|