open-agents-ai 0.187.515 → 0.187.516
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/index.js +251 -4
- package/dist/postinstall-daemon.cjs +121 -4
- package/npm-shrinkwrap.json +8 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -595998,6 +595998,159 @@ function checkConcurrentJobLimit(auth) {
|
|
|
595998
595998
|
}
|
|
595999
595999
|
return null;
|
|
596000
596000
|
}
|
|
596001
|
+
function adoptHandoffRuns() {
|
|
596002
|
+
if (process.env["OA_DISABLE_RUN_HANDOFF"] === "1") return;
|
|
596003
|
+
const fs7 = require3("node:fs");
|
|
596004
|
+
const path8 = require3("node:path");
|
|
596005
|
+
const home = require3("node:os").homedir();
|
|
596006
|
+
const handoffPath2 = path8.join(home, ".open-agents", "runs-handoff.json");
|
|
596007
|
+
if (!fs7.existsSync(handoffPath2)) return;
|
|
596008
|
+
let handoff = {};
|
|
596009
|
+
try {
|
|
596010
|
+
const raw = fs7.readFileSync(handoffPath2, "utf-8");
|
|
596011
|
+
handoff = JSON.parse(raw);
|
|
596012
|
+
} catch (e2) {
|
|
596013
|
+
process.stderr.write(` WARN: handoff file unreadable (${e2.message}) — discarding.
|
|
596014
|
+
`);
|
|
596015
|
+
try {
|
|
596016
|
+
fs7.unlinkSync(handoffPath2);
|
|
596017
|
+
} catch {
|
|
596018
|
+
}
|
|
596019
|
+
return;
|
|
596020
|
+
}
|
|
596021
|
+
try {
|
|
596022
|
+
fs7.unlinkSync(handoffPath2);
|
|
596023
|
+
} catch {
|
|
596024
|
+
}
|
|
596025
|
+
const runs = handoff.runs ?? [];
|
|
596026
|
+
if (runs.length === 0) return;
|
|
596027
|
+
const adopted = [];
|
|
596028
|
+
const orphaned = [];
|
|
596029
|
+
for (const r2 of runs) {
|
|
596030
|
+
if (!r2.pid || r2.pid <= 0) continue;
|
|
596031
|
+
let alive = false;
|
|
596032
|
+
try {
|
|
596033
|
+
process.kill(r2.pid, 0);
|
|
596034
|
+
alive = true;
|
|
596035
|
+
} catch {
|
|
596036
|
+
alive = false;
|
|
596037
|
+
}
|
|
596038
|
+
const jobsDir3 = path8.join(home, ".open-agents", "jobs");
|
|
596039
|
+
const jobFile = path8.join(jobsDir3, `${r2.jobId}.json`);
|
|
596040
|
+
let job = null;
|
|
596041
|
+
try {
|
|
596042
|
+
if (fs7.existsSync(jobFile)) job = JSON.parse(fs7.readFileSync(jobFile, "utf-8"));
|
|
596043
|
+
} catch {
|
|
596044
|
+
}
|
|
596045
|
+
if (!alive) {
|
|
596046
|
+
orphaned.push(r2.jobId);
|
|
596047
|
+
if (job) {
|
|
596048
|
+
let finalJson = "";
|
|
596049
|
+
try {
|
|
596050
|
+
if (r2.outputFile && fs7.existsSync(r2.outputFile)) {
|
|
596051
|
+
const raw = fs7.readFileSync(r2.outputFile, "utf-8");
|
|
596052
|
+
const lines = raw.trim().split("\n");
|
|
596053
|
+
for (let i2 = lines.length - 1; i2 >= 0; i2--) {
|
|
596054
|
+
if (lines[i2].trimEnd() === "{" || lines[i2].startsWith("{") && !lines[i2].startsWith('{"type":"tool_call"')) {
|
|
596055
|
+
finalJson = lines.slice(i2).join("\n");
|
|
596056
|
+
break;
|
|
596057
|
+
}
|
|
596058
|
+
}
|
|
596059
|
+
}
|
|
596060
|
+
} catch {
|
|
596061
|
+
}
|
|
596062
|
+
try {
|
|
596063
|
+
const parsed = finalJson ? JSON.parse(finalJson) : null;
|
|
596064
|
+
if (parsed && parsed.status) job.status = parsed.status;
|
|
596065
|
+
else job.status = "failed";
|
|
596066
|
+
job.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
596067
|
+
if (parsed?.summary) job.summary = parsed.summary;
|
|
596068
|
+
if (parsed?.error) job.error = parsed.error;
|
|
596069
|
+
job.adoptedAs = "orphaned";
|
|
596070
|
+
} catch {
|
|
596071
|
+
job.status = "failed";
|
|
596072
|
+
job.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
596073
|
+
job.adoptedAs = "orphaned";
|
|
596074
|
+
}
|
|
596075
|
+
try {
|
|
596076
|
+
fs7.writeFileSync(jobFile, JSON.stringify(job, null, 2), "utf-8");
|
|
596077
|
+
} catch {
|
|
596078
|
+
}
|
|
596079
|
+
}
|
|
596080
|
+
continue;
|
|
596081
|
+
}
|
|
596082
|
+
adopted.push(r2.jobId);
|
|
596083
|
+
const adoptedShim = {
|
|
596084
|
+
pid: r2.pid,
|
|
596085
|
+
// Stub stdio (we never read from it again — the child writes directly
|
|
596086
|
+
// to the on-disk output file which /v1/runs/{id}/output already reads).
|
|
596087
|
+
stdout: { on: () => {
|
|
596088
|
+
} },
|
|
596089
|
+
stderr: { on: () => {
|
|
596090
|
+
} },
|
|
596091
|
+
__adopted: true
|
|
596092
|
+
};
|
|
596093
|
+
runningProcesses.set(r2.jobId, adoptedShim);
|
|
596094
|
+
if (job) {
|
|
596095
|
+
job.adoptedAs = "live";
|
|
596096
|
+
job.adoptedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
596097
|
+
try {
|
|
596098
|
+
fs7.writeFileSync(jobFile, JSON.stringify(job, null, 2), "utf-8");
|
|
596099
|
+
} catch {
|
|
596100
|
+
}
|
|
596101
|
+
}
|
|
596102
|
+
const pollHandle = setInterval(() => {
|
|
596103
|
+
let stillAlive = false;
|
|
596104
|
+
try {
|
|
596105
|
+
process.kill(r2.pid, 0);
|
|
596106
|
+
stillAlive = true;
|
|
596107
|
+
} catch {
|
|
596108
|
+
stillAlive = false;
|
|
596109
|
+
}
|
|
596110
|
+
if (stillAlive) return;
|
|
596111
|
+
clearInterval(pollHandle);
|
|
596112
|
+
runningProcesses.delete(r2.jobId);
|
|
596113
|
+
try {
|
|
596114
|
+
const jf = jobFile;
|
|
596115
|
+
if (!fs7.existsSync(jf)) return;
|
|
596116
|
+
const j = JSON.parse(fs7.readFileSync(jf, "utf-8"));
|
|
596117
|
+
if (j.status !== "running") return;
|
|
596118
|
+
let finalJson = "";
|
|
596119
|
+
try {
|
|
596120
|
+
if (r2.outputFile && fs7.existsSync(r2.outputFile)) {
|
|
596121
|
+
const raw = fs7.readFileSync(r2.outputFile, "utf-8");
|
|
596122
|
+
const lines = raw.trim().split("\n");
|
|
596123
|
+
for (let i2 = lines.length - 1; i2 >= 0; i2--) {
|
|
596124
|
+
if (lines[i2].trimEnd() === "{" || lines[i2].startsWith("{") && !lines[i2].startsWith('{"type":"tool_call"')) {
|
|
596125
|
+
finalJson = lines.slice(i2).join("\n");
|
|
596126
|
+
break;
|
|
596127
|
+
}
|
|
596128
|
+
}
|
|
596129
|
+
}
|
|
596130
|
+
} catch {
|
|
596131
|
+
}
|
|
596132
|
+
try {
|
|
596133
|
+
const parsed = finalJson ? JSON.parse(finalJson) : null;
|
|
596134
|
+
if (parsed && parsed.status) j.status = parsed.status;
|
|
596135
|
+
else j.status = "completed";
|
|
596136
|
+
j.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
596137
|
+
if (parsed?.summary) j.summary = parsed.summary;
|
|
596138
|
+
if (parsed?.error) j.error = parsed.error;
|
|
596139
|
+
fs7.writeFileSync(jf, JSON.stringify(j, null, 2), "utf-8");
|
|
596140
|
+
} catch {
|
|
596141
|
+
}
|
|
596142
|
+
} catch {
|
|
596143
|
+
}
|
|
596144
|
+
}, 3e3);
|
|
596145
|
+
pollHandle.unref();
|
|
596146
|
+
}
|
|
596147
|
+
process.stderr.write(` Adopted ${adopted.length} live run(s) from previous daemon, ${orphaned.length} orphaned.
|
|
596148
|
+
`);
|
|
596149
|
+
if (adopted.length > 0) process.stderr.write(` live: ${adopted.join(", ")}
|
|
596150
|
+
`);
|
|
596151
|
+
if (orphaned.length > 0) process.stderr.write(` orphaned: ${orphaned.join(", ")}
|
|
596152
|
+
`);
|
|
596153
|
+
}
|
|
596001
596154
|
function incrementActiveJobs(user) {
|
|
596002
596155
|
if (!user) return;
|
|
596003
596156
|
getKeyUsage(user).activeJobs++;
|
|
@@ -601614,6 +601767,12 @@ function startApiServer(options2 = {}) {
|
|
|
601614
601767
|
`);
|
|
601615
601768
|
log22(` Primary: ${config.backendUrl} (${config.backendType || "ollama"})
|
|
601616
601769
|
`);
|
|
601770
|
+
try {
|
|
601771
|
+
adoptHandoffRuns();
|
|
601772
|
+
} catch (e2) {
|
|
601773
|
+
log22(` WARN: handoff adoption crashed: ${e2.message}
|
|
601774
|
+
`);
|
|
601775
|
+
}
|
|
601617
601776
|
const _retCheck = process.env["OA_RUN_RETENTION_H"];
|
|
601618
601777
|
const _retOff = _retCheck === "0";
|
|
601619
601778
|
if (!_retOff) {
|
|
@@ -601660,8 +601819,96 @@ function startApiServer(options2 = {}) {
|
|
|
601660
601819
|
}).catch(() => {
|
|
601661
601820
|
});
|
|
601662
601821
|
});
|
|
601663
|
-
const shutdown = () => {
|
|
601664
|
-
|
|
601822
|
+
const shutdown = (signal) => {
|
|
601823
|
+
const hardKill = signal === "SIGINT" || process.env["OA_DAEMON_HARD_SHUTDOWN"] === "1";
|
|
601824
|
+
log22(`
|
|
601825
|
+
Shutting down API server (signal=${signal}, mode=${hardKill ? "hard-kill" : "graceful-handoff"}) ...
|
|
601826
|
+
`);
|
|
601827
|
+
if (!hardKill && runningProcesses.size > 0) {
|
|
601828
|
+
try {
|
|
601829
|
+
const handoffPath2 = (() => {
|
|
601830
|
+
try {
|
|
601831
|
+
const home = require3("node:os").homedir();
|
|
601832
|
+
return require3("node:path").join(home, ".open-agents", "runs-handoff.json");
|
|
601833
|
+
} catch {
|
|
601834
|
+
return "";
|
|
601835
|
+
}
|
|
601836
|
+
})();
|
|
601837
|
+
if (handoffPath2) {
|
|
601838
|
+
const handoff = [];
|
|
601839
|
+
for (const [id, child] of runningProcesses) {
|
|
601840
|
+
const pid = child.pid;
|
|
601841
|
+
if (!pid || pid <= 0) continue;
|
|
601842
|
+
let outputFile = null;
|
|
601843
|
+
let startedAt2 = null;
|
|
601844
|
+
let user = null;
|
|
601845
|
+
let scope = null;
|
|
601846
|
+
let cwd5 = null;
|
|
601847
|
+
try {
|
|
601848
|
+
const fs7 = require3("node:fs");
|
|
601849
|
+
const path8 = require3("node:path");
|
|
601850
|
+
const home = require3("node:os").homedir();
|
|
601851
|
+
const jobsDir3 = path8.join(home, ".open-agents", "jobs");
|
|
601852
|
+
const jobFile = path8.join(jobsDir3, `${id}.json`);
|
|
601853
|
+
if (fs7.existsSync(jobFile)) {
|
|
601854
|
+
const j = JSON.parse(fs7.readFileSync(jobFile, "utf-8"));
|
|
601855
|
+
outputFile = j.outputFile ?? path8.join(jobsDir3, `${id}.output`);
|
|
601856
|
+
startedAt2 = j.startedAt ?? null;
|
|
601857
|
+
user = j.user ?? null;
|
|
601858
|
+
scope = j.scope ?? null;
|
|
601859
|
+
cwd5 = j.cwd ?? null;
|
|
601860
|
+
}
|
|
601861
|
+
} catch {
|
|
601862
|
+
}
|
|
601863
|
+
handoff.push({ jobId: id, pid, outputFile, startedAt: startedAt2, user, scope, cwd: cwd5, handoffAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
601864
|
+
}
|
|
601865
|
+
if (handoff.length > 0) {
|
|
601866
|
+
try {
|
|
601867
|
+
const fs7 = require3("node:fs");
|
|
601868
|
+
const path8 = require3("node:path");
|
|
601869
|
+
const home = require3("node:os").homedir();
|
|
601870
|
+
const dir = path8.join(home, ".open-agents");
|
|
601871
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
601872
|
+
fs7.writeFileSync(handoffPath2, JSON.stringify({
|
|
601873
|
+
writtenAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
601874
|
+
fromPid: process.pid,
|
|
601875
|
+
runs: handoff
|
|
601876
|
+
}, null, 2), "utf-8");
|
|
601877
|
+
log22(` Wrote handoff for ${handoff.length} in-flight run(s) → ${handoffPath2}
|
|
601878
|
+
`);
|
|
601879
|
+
log22(` Children will continue running; next daemon will adopt them.
|
|
601880
|
+
`);
|
|
601881
|
+
} catch (e2) {
|
|
601882
|
+
log22(` WARN: failed to write handoff (${e2.message}) — falling back to hard-kill.
|
|
601883
|
+
`);
|
|
601884
|
+
for (const [id, child] of runningProcesses) {
|
|
601885
|
+
const pid = child.pid;
|
|
601886
|
+
if (pid && pid > 0) {
|
|
601887
|
+
try {
|
|
601888
|
+
process.kill(-pid, "SIGTERM");
|
|
601889
|
+
} catch {
|
|
601890
|
+
}
|
|
601891
|
+
try {
|
|
601892
|
+
process.kill(pid, "SIGTERM");
|
|
601893
|
+
} catch {
|
|
601894
|
+
}
|
|
601895
|
+
}
|
|
601896
|
+
runningProcesses.delete(id);
|
|
601897
|
+
}
|
|
601898
|
+
}
|
|
601899
|
+
}
|
|
601900
|
+
}
|
|
601901
|
+
} catch (e2) {
|
|
601902
|
+
log22(` WARN: handoff block crashed (${e2.message}) — proceeding to server.close()
|
|
601903
|
+
`);
|
|
601904
|
+
}
|
|
601905
|
+
server2.close(() => {
|
|
601906
|
+
log22(" Server stopped (children preserved for adoption).\n");
|
|
601907
|
+
process.exit(0);
|
|
601908
|
+
});
|
|
601909
|
+
setTimeout(() => process.exit(0), 8e3).unref();
|
|
601910
|
+
return;
|
|
601911
|
+
}
|
|
601665
601912
|
for (const [id, child] of runningProcesses) {
|
|
601666
601913
|
const pid = child.pid;
|
|
601667
601914
|
if (pid && pid > 0) {
|
|
@@ -601697,8 +601944,8 @@ function startApiServer(options2 = {}) {
|
|
|
601697
601944
|
});
|
|
601698
601945
|
setTimeout(() => process.exit(1), 5e3).unref();
|
|
601699
601946
|
};
|
|
601700
|
-
process.on("SIGINT", shutdown);
|
|
601701
|
-
process.on("SIGTERM", shutdown);
|
|
601947
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
601948
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
601702
601949
|
return server2;
|
|
601703
601950
|
}
|
|
601704
601951
|
async function handleVisionEmbed(req2, res) {
|
|
@@ -145,6 +145,105 @@ function effectiveUser() {
|
|
|
145
145
|
return process.env.USER || process.env.LOGNAME || os.userInfo().username;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
// ─── Force-kill port holder (regardless of how it was launched) ────────────
|
|
149
|
+
//
|
|
150
|
+
// Critical for upgrade correctness: when an OLD daemon (started before this
|
|
151
|
+
// install) is running, it holds port 11435 with stale in-memory code. The
|
|
152
|
+
// systemctl/launchctl `restart` calls below CANNOT reach it because the
|
|
153
|
+
// service manager doesn't own that process — it was started detached by
|
|
154
|
+
// `oa serve --daemon` from a previous TUI session.
|
|
155
|
+
//
|
|
156
|
+
// Result without this kill: postinstall tries to start a NEW systemd
|
|
157
|
+
// daemon, port-bind fails, old daemon stays alive serving stale code,
|
|
158
|
+
// and the user's runs continue with broken patches.
|
|
159
|
+
//
|
|
160
|
+
// Strategy (matches packages/cli/src/daemon.ts:forceKillDaemon):
|
|
161
|
+
// 1. SIGTERM via known PID files (~/.open-agents/daemon.pid + .oa/nexus/daemon.pid)
|
|
162
|
+
// 2. lsof / fuser port probe — find ANY other holder
|
|
163
|
+
// 3. 2s graceful grace, then SIGKILL stragglers
|
|
164
|
+
// 4. Poll /health up to 5s for confirmation
|
|
165
|
+
//
|
|
166
|
+
// Skips when OA_DISABLE_FORCE_KILL_DAEMON=1 (escape valve).
|
|
167
|
+
function forceKillPortHolder(port, cb) {
|
|
168
|
+
if (process.env.OA_DISABLE_FORCE_KILL_DAEMON === "1") {
|
|
169
|
+
log("OA_DISABLE_FORCE_KILL_DAEMON=1 — skipping port-holder kill (upgrades may not pick up new code).");
|
|
170
|
+
return cb(0);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
var killed = 0;
|
|
174
|
+
|
|
175
|
+
// Step 1: SIGTERM via PID files (graceful)
|
|
176
|
+
var pidFiles = [
|
|
177
|
+
path.join(HOME, ".open-agents", "daemon.pid"),
|
|
178
|
+
path.join(process.cwd(), ".oa", "nexus", "daemon.pid"),
|
|
179
|
+
];
|
|
180
|
+
pidFiles.forEach(function (pidFile) {
|
|
181
|
+
try {
|
|
182
|
+
if (!fs.existsSync(pidFile)) return;
|
|
183
|
+
var n = parseInt(fs.readFileSync(pidFile, "utf8").trim(), 10);
|
|
184
|
+
if (!n || n <= 0) return;
|
|
185
|
+
try { process.kill(n, "SIGTERM"); killed++; log("SIGTERM old daemon (pid " + n + ", from " + pidFile + ")"); } catch (e) { /* dead */ }
|
|
186
|
+
} catch (e) { /* */ }
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Step 2: port probe — lsof/fuser to find ANY pid holding the port
|
|
190
|
+
try {
|
|
191
|
+
var out = "";
|
|
192
|
+
try {
|
|
193
|
+
out = cp.execSync("lsof -ti :" + port + " 2>/dev/null || fuser " + port + "/tcp 2>/dev/null || true", {
|
|
194
|
+
encoding: "utf8", timeout: 3000,
|
|
195
|
+
}).trim();
|
|
196
|
+
} catch (e) { /* tools unavailable */ }
|
|
197
|
+
if (out) {
|
|
198
|
+
var pids = out.split(/[\s\n]+/).map(function (s) { return parseInt(s, 10); }).filter(function (n) {
|
|
199
|
+
return Number.isFinite(n) && n > 0 && n !== process.pid;
|
|
200
|
+
});
|
|
201
|
+
pids.forEach(function (otherPid) {
|
|
202
|
+
try { process.kill(otherPid, "SIGTERM"); killed++; log("SIGTERM port-holder (pid " + otherPid + ")"); } catch (e) { /* */ }
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
} catch (e) { /* */ }
|
|
206
|
+
|
|
207
|
+
if (killed === 0) {
|
|
208
|
+
// Nothing to kill — port was already free.
|
|
209
|
+
return cb(0);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Step 3: 2s grace, then SIGKILL stragglers
|
|
213
|
+
setTimeout(function () {
|
|
214
|
+
try {
|
|
215
|
+
var out = cp.execSync("lsof -ti :" + port + " 2>/dev/null || fuser " + port + "/tcp 2>/dev/null || true", {
|
|
216
|
+
encoding: "utf8", timeout: 3000,
|
|
217
|
+
}).trim();
|
|
218
|
+
if (out) {
|
|
219
|
+
var pids = out.split(/[\s\n]+/).map(function (s) { return parseInt(s, 10); }).filter(function (n) {
|
|
220
|
+
return Number.isFinite(n) && n > 0 && n !== process.pid;
|
|
221
|
+
});
|
|
222
|
+
pids.forEach(function (otherPid) {
|
|
223
|
+
try { process.kill(otherPid, "SIGKILL"); log("SIGKILL straggler (pid " + otherPid + ")"); } catch (e) { /* */ }
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
} catch (e) { /* */ }
|
|
227
|
+
|
|
228
|
+
// Step 4: poll until port is free (up to 5s) — required so the next
|
|
229
|
+
// service-manager restart binds without conflict.
|
|
230
|
+
var pollStart = Date.now();
|
|
231
|
+
function pollFree() {
|
|
232
|
+
if (Date.now() - pollStart > 5000) return cb(killed);
|
|
233
|
+
tryHealth(port, function (alive) {
|
|
234
|
+
if (!alive) return cb(killed);
|
|
235
|
+
setTimeout(pollFree, 200);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
pollFree();
|
|
239
|
+
|
|
240
|
+
// Clean up PID files now that the processes are dead
|
|
241
|
+
pidFiles.forEach(function (pidFile) {
|
|
242
|
+
try { if (fs.existsSync(pidFile)) fs.unlinkSync(pidFile); } catch (e) { /* */ }
|
|
243
|
+
});
|
|
244
|
+
}, 2000);
|
|
245
|
+
}
|
|
246
|
+
|
|
148
247
|
// ─── Nexus cleanup (preserve prior postinstall behaviour) ──────────────────
|
|
149
248
|
|
|
150
249
|
function cleanNexus() {
|
|
@@ -572,7 +671,25 @@ function main() {
|
|
|
572
671
|
return safeExit(0);
|
|
573
672
|
}
|
|
574
673
|
|
|
674
|
+
// Force-kill any process holding the daemon port BEFORE we try to install
|
|
675
|
+
// a service. This handles the orphan-detached-daemon case (started by
|
|
676
|
+
// `oa serve --daemon` from a previous TUI session) — systemctl/launchctl
|
|
677
|
+
// restart can't reach it, so we have to clean up explicitly. Without this,
|
|
678
|
+
// the new service-managed daemon fails to bind port 11435 and the user
|
|
679
|
+
// ends up running stale in-memory code from the previous version.
|
|
680
|
+
forceKillPortHolder(PORT, function (killedCount) {
|
|
681
|
+
if (killedCount > 0) {
|
|
682
|
+
log("Killed " + killedCount + " stale daemon process(es) holding port " + PORT + ".");
|
|
683
|
+
}
|
|
684
|
+
runMainAfterKill();
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
function runMainAfterKill() {
|
|
575
689
|
// Fast path: daemon already answering /health on the target port.
|
|
690
|
+
// After forceKillPortHolder, this should be false unless a service manager
|
|
691
|
+
// already auto-restarted (rare race). We still check so the rest of the
|
|
692
|
+
// flow stays idempotent.
|
|
576
693
|
tryHealth(PORT, function (alreadyUp) {
|
|
577
694
|
var user = effectiveUser();
|
|
578
695
|
var nodeBin = resolveNodeBinary();
|
|
@@ -584,10 +701,10 @@ function main() {
|
|
|
584
701
|
}
|
|
585
702
|
|
|
586
703
|
if (alreadyUp) {
|
|
587
|
-
log("OA daemon
|
|
588
|
-
//
|
|
589
|
-
//
|
|
590
|
-
//
|
|
704
|
+
log("OA daemon answering /health on port " + PORT + " — re-checking version drift before service (re)install.");
|
|
705
|
+
// Note: we already force-killed any stale process — anything answering
|
|
706
|
+
// now is a fresh service-managed daemon. Service (re)install below is
|
|
707
|
+
// idempotent and ensures the unit file matches the current bundle.
|
|
591
708
|
}
|
|
592
709
|
|
|
593
710
|
log("Installing OA API daemon service for port " + PORT + " ...");
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-agents-ai",
|
|
3
|
-
"version": "0.187.
|
|
3
|
+
"version": "0.187.516",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "open-agents-ai",
|
|
9
|
-
"version": "0.187.
|
|
9
|
+
"version": "0.187.516",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "CC-BY-NC-4.0",
|
|
12
12
|
"dependencies": {
|
|
@@ -2081,9 +2081,9 @@
|
|
|
2081
2081
|
}
|
|
2082
2082
|
},
|
|
2083
2083
|
"node_modules/b4a": {
|
|
2084
|
-
"version": "1.8.
|
|
2085
|
-
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.
|
|
2086
|
-
"integrity": "sha512-
|
|
2084
|
+
"version": "1.8.1",
|
|
2085
|
+
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz",
|
|
2086
|
+
"integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==",
|
|
2087
2087
|
"license": "Apache-2.0",
|
|
2088
2088
|
"optional": true,
|
|
2089
2089
|
"peerDependencies": {
|
|
@@ -4789,9 +4789,9 @@
|
|
|
4789
4789
|
"license": "Apache-2.0 OR MIT"
|
|
4790
4790
|
},
|
|
4791
4791
|
"node_modules/nanoid": {
|
|
4792
|
-
"version": "5.1.
|
|
4793
|
-
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.
|
|
4794
|
-
"integrity": "sha512-
|
|
4792
|
+
"version": "5.1.11",
|
|
4793
|
+
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.11.tgz",
|
|
4794
|
+
"integrity": "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg==",
|
|
4795
4795
|
"funding": [
|
|
4796
4796
|
{
|
|
4797
4797
|
"type": "github",
|
package/package.json
CHANGED