@chipschat/hermes 1.0.5 → 1.0.7
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/README.md +25 -3
- package/dist/THIRD-PARTY-NOTICES.md +1 -1
- package/dist/main.js +645 -141
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -9,10 +9,10 @@ var __export = (target, all) => {
|
|
|
9
9
|
|
|
10
10
|
// src/main.ts
|
|
11
11
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
12
|
-
import { existsSync as
|
|
12
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
|
|
13
13
|
import { createServer as createServer2 } from "node:net";
|
|
14
|
-
import { homedir as
|
|
15
|
-
import { dirname as
|
|
14
|
+
import { homedir as homedir8 } from "node:os";
|
|
15
|
+
import { dirname as dirname6, join as join12, resolve as resolve6 } from "node:path";
|
|
16
16
|
|
|
17
17
|
// node_modules/zod/v4/classic/external.js
|
|
18
18
|
var external_exports = {};
|
|
@@ -2024,8 +2024,8 @@ function emoji() {
|
|
|
2024
2024
|
}
|
|
2025
2025
|
var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
|
|
2026
2026
|
var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
|
|
2027
|
-
var mac = (
|
|
2028
|
-
const escapedDelim = escapeRegex(
|
|
2027
|
+
var mac = (delimiter3) => {
|
|
2028
|
+
const escapedDelim = escapeRegex(delimiter3 ?? ":");
|
|
2029
2029
|
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
|
|
2030
2030
|
};
|
|
2031
2031
|
var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
|
|
@@ -2599,8 +2599,8 @@ var Doc = class {
|
|
|
2599
2599
|
const lines2 = content.split("\n").filter((x) => x);
|
|
2600
2600
|
const minIndent = Math.min(...lines2.map((x) => x.length - x.trimStart().length));
|
|
2601
2601
|
const dedented = lines2.map((x) => x.slice(minIndent)).map((x) => " ".repeat(this.indent * 2) + x);
|
|
2602
|
-
for (const
|
|
2603
|
-
this.content.push(
|
|
2602
|
+
for (const line2 of dedented) {
|
|
2603
|
+
this.content.push(line2);
|
|
2604
2604
|
}
|
|
2605
2605
|
}
|
|
2606
2606
|
compile() {
|
|
@@ -5081,7 +5081,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
|
|
|
5081
5081
|
var NONE = 0;
|
|
5082
5082
|
var ASSUMED = 1;
|
|
5083
5083
|
var PROVEN = 2;
|
|
5084
|
-
function isRecursive(inst, stack,
|
|
5084
|
+
function isRecursive(inst, stack, resolve7) {
|
|
5085
5085
|
const cached2 = recursive.get(inst);
|
|
5086
5086
|
if (cached2 !== void 0)
|
|
5087
5087
|
return cached2 ? PROVEN : NONE;
|
|
@@ -5091,7 +5091,7 @@ function isRecursive(inst, stack, resolve6) {
|
|
|
5091
5091
|
let result = NONE;
|
|
5092
5092
|
const check2 = (child) => {
|
|
5093
5093
|
if (result !== PROVEN && child?._zod) {
|
|
5094
|
-
const answer = isRecursive(child, stack,
|
|
5094
|
+
const answer = isRecursive(child, stack, resolve7);
|
|
5095
5095
|
if (answer > result)
|
|
5096
5096
|
result = answer;
|
|
5097
5097
|
}
|
|
@@ -5102,7 +5102,7 @@ function isRecursive(inst, stack, resolve6) {
|
|
|
5102
5102
|
const desc = Object.getOwnPropertyDescriptor(sh, key);
|
|
5103
5103
|
if (spread && !desc.enumerable)
|
|
5104
5104
|
continue;
|
|
5105
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
5105
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve7) : NONE;
|
|
5106
5106
|
if (child > answer)
|
|
5107
5107
|
answer = child;
|
|
5108
5108
|
}
|
|
@@ -5166,7 +5166,7 @@ function isRecursive(inst, stack, resolve6) {
|
|
|
5166
5166
|
break;
|
|
5167
5167
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
5168
5168
|
case "lazy": {
|
|
5169
|
-
const inner = def._cachedInner ?? (
|
|
5169
|
+
const inner = def._cachedInner ?? (resolve7 ? inst._zod.innerType : void 0);
|
|
5170
5170
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
5171
5171
|
break;
|
|
5172
5172
|
}
|
|
@@ -20535,31 +20535,31 @@ function withRole(name2, from) {
|
|
|
20535
20535
|
function flat(text) {
|
|
20536
20536
|
return text.replace(/\s*\n\s*/g, " ").trim();
|
|
20537
20537
|
}
|
|
20538
|
-
function wordsOf(
|
|
20539
|
-
const text = flat(
|
|
20540
|
-
const heard =
|
|
20538
|
+
function wordsOf(line2) {
|
|
20539
|
+
const text = flat(line2.text ?? "");
|
|
20540
|
+
const heard = line2.transcript === void 0 ? "" : flat(line2.transcript);
|
|
20541
20541
|
const parts = [text];
|
|
20542
20542
|
if (heard !== "")
|
|
20543
20543
|
parts.push(text === "" ? `(voice note) ${heard}` : `(voice note: ${heard})`);
|
|
20544
|
-
if (heard === "" &&
|
|
20545
|
-
parts.push(`(sent ${
|
|
20546
|
-
if (
|
|
20544
|
+
if (heard === "" && line2.files !== void 0)
|
|
20545
|
+
parts.push(`(sent ${line2.files.join(", ")})`);
|
|
20546
|
+
if (line2.held === true)
|
|
20547
20547
|
parts.push("(held by Chips until a person acts)");
|
|
20548
20548
|
return parts.filter((part) => part !== "").join(" ");
|
|
20549
20549
|
}
|
|
20550
|
-
function renderLine(
|
|
20551
|
-
const name2 =
|
|
20552
|
-
switch (
|
|
20550
|
+
function renderLine(line2) {
|
|
20551
|
+
const name2 = line2.from.name;
|
|
20552
|
+
switch (line2.kind) {
|
|
20553
20553
|
case "said":
|
|
20554
|
-
return `${withRole(name2,
|
|
20554
|
+
return `${withRole(name2, line2.from)}: ${wordsOf(line2)}`;
|
|
20555
20555
|
case "joined":
|
|
20556
20556
|
return `(${name2} joined the space)`;
|
|
20557
20557
|
case "left":
|
|
20558
20558
|
return `(${name2} left the space)`;
|
|
20559
20559
|
case "renamed":
|
|
20560
|
-
if (
|
|
20561
|
-
return `(${name2} renamed the space "${
|
|
20562
|
-
return `(${
|
|
20560
|
+
if (line2.title !== void 0)
|
|
20561
|
+
return `(${name2} renamed the space "${line2.title}")`;
|
|
20562
|
+
return `(${line2.previous ?? name2} is now called ${line2.name ?? name2})`;
|
|
20563
20563
|
case "archived":
|
|
20564
20564
|
return `(${name2} archived the space)`;
|
|
20565
20565
|
default:
|
|
@@ -20610,7 +20610,7 @@ function quoteLines(replying) {
|
|
|
20610
20610
|
body.push(`(sent ${replying.files.join(", ")})`);
|
|
20611
20611
|
}
|
|
20612
20612
|
}
|
|
20613
|
-
return body.map((
|
|
20613
|
+
return body.map((line2) => line2 === "" ? ">" : `> ${line2}`);
|
|
20614
20614
|
}
|
|
20615
20615
|
var DAY_MS = 864e5;
|
|
20616
20616
|
function saidWhen(at, reading) {
|
|
@@ -22517,7 +22517,7 @@ var AGENT_FIRST_NAMES = [
|
|
|
22517
22517
|
"Xander Xavi Xavier",
|
|
22518
22518
|
"Yann Yaro Yosef Yuri Yusuf Yves",
|
|
22519
22519
|
"Zaid Zane Zeke Zeno Zev Ziggy"
|
|
22520
|
-
].flatMap((
|
|
22520
|
+
].flatMap((line2) => line2.split(" "));
|
|
22521
22521
|
|
|
22522
22522
|
// node_modules/@chipschat/protocol/dist/src/frames.js
|
|
22523
22523
|
var clientHelloFrame = external_exports.object({
|
|
@@ -22846,8 +22846,8 @@ function createOutbox() {
|
|
|
22846
22846
|
const waiting = /* @__PURE__ */ new Map();
|
|
22847
22847
|
return {
|
|
22848
22848
|
add(key, event) {
|
|
22849
|
-
return new Promise((
|
|
22850
|
-
waiting.set(key, { event, resolve:
|
|
22849
|
+
return new Promise((resolve7, reject) => {
|
|
22850
|
+
waiting.set(key, { event, resolve: resolve7, reject });
|
|
22851
22851
|
});
|
|
22852
22852
|
},
|
|
22853
22853
|
unanswered: () => [...waiting.values()].map((entry) => entry.event),
|
|
@@ -22923,7 +22923,7 @@ async function inPieces(call, path, bytes) {
|
|
|
22923
22923
|
async function afterLoss(error62, failures) {
|
|
22924
22924
|
if (error62 instanceof RefusedError || failures >= PIECE_TRIES)
|
|
22925
22925
|
throw error62;
|
|
22926
|
-
await new Promise((
|
|
22926
|
+
await new Promise((resolve7) => setTimeout(resolve7, 250 * failures));
|
|
22927
22927
|
}
|
|
22928
22928
|
function sendPiece(call, path, bytes, offset) {
|
|
22929
22929
|
const piece = bytes.subarray(offset, Math.min(offset + PIECE_BYTES, bytes.byteLength));
|
|
@@ -22949,16 +22949,16 @@ var TRIES = 4;
|
|
|
22949
22949
|
var MARKS = /^(<<<<<<<|>>>>>>>) /m;
|
|
22950
22950
|
var REJECTED = /\[rejected\]|fetch first|non-fast-forward/;
|
|
22951
22951
|
var OWN_CONFIG = /^(core\.(repositoryformatversion|filemode|bare|logallrefupdates|ignorecase|precomposeunicode|symlinks)|remote\.origin\.(url|fetch)|branch\..+\.(remote|merge)|user\.(name|email))$/;
|
|
22952
|
-
var lines = (said2) => said2.split("\n").filter((
|
|
22952
|
+
var lines = (said2) => said2.split("\n").filter((line2) => line2 !== "");
|
|
22953
22953
|
var unique = (names) => [...new Set(names)];
|
|
22954
22954
|
function subjectOf(words4) {
|
|
22955
|
-
const first = words4.split("\n").find((
|
|
22955
|
+
const first = words4.split("\n").find((line2) => line2.trim() !== "")?.trim() ?? "";
|
|
22956
22956
|
if (first === "")
|
|
22957
22957
|
return "A change to the project";
|
|
22958
22958
|
return first.length <= 72 ? first : `${first.slice(0, 71)}\u2026`;
|
|
22959
22959
|
}
|
|
22960
22960
|
function remoteWords(err) {
|
|
22961
|
-
const said2 = lines(err).filter((
|
|
22961
|
+
const said2 = lines(err).filter((line2) => line2.startsWith("remote: ")).map((line2) => line2.slice("remote: ".length).trim()).filter((line2) => line2 !== "" && !/^error: hook declined/.test(line2));
|
|
22962
22962
|
return said2.length > 0 ? said2.join(" ") : lines(err)[0] ?? "git said nothing";
|
|
22963
22963
|
}
|
|
22964
22964
|
function versionIn(said2) {
|
|
@@ -23007,13 +23007,13 @@ function heldNote(pushed, name2) {
|
|
|
23007
23007
|
}
|
|
23008
23008
|
async function checkGit(git2 = "git", path = process.env["PATH"] ?? "/usr/bin:/bin") {
|
|
23009
23009
|
const [major, minor] = versionIn(PROJECT_GIT_VERSION) ?? [0, 0];
|
|
23010
|
-
return new Promise((
|
|
23010
|
+
return new Promise((resolve7) => {
|
|
23011
23011
|
execFile(git2, ["--version"], { env: { PATH: path } }, (error62, stdout) => {
|
|
23012
23012
|
if (error62 !== null)
|
|
23013
|
-
return
|
|
23013
|
+
return resolve7({ ok: false, why: "git was not found on this machine" });
|
|
23014
23014
|
const mine = versionIn(stdout);
|
|
23015
23015
|
const enough = mine !== void 0 && (mine[0] > major || mine[0] === major && mine[1] >= minor);
|
|
23016
|
-
|
|
23016
|
+
resolve7(enough ? { ok: true, version: mine.join(".") } : { ok: false, why: `${stdout.trim()} is older than ${PROJECT_GIT_VERSION}` });
|
|
23017
23017
|
});
|
|
23018
23018
|
});
|
|
23019
23019
|
}
|
|
@@ -23142,8 +23142,8 @@ var Project = class {
|
|
|
23142
23142
|
async standing(remote) {
|
|
23143
23143
|
const mine = await this.head("HEAD");
|
|
23144
23144
|
const base = await this.baseOf(mine, remote);
|
|
23145
|
-
const counted = lines((await this.git(["diff", "--numstat", "--no-renames", "-z", base])).out.replace(/\0/g, "\n")).map((
|
|
23146
|
-
const [added2 = "0", removed = "0", ...path] =
|
|
23145
|
+
const counted = lines((await this.git(["diff", "--numstat", "--no-renames", "-z", base])).out.replace(/\0/g, "\n")).map((line2) => {
|
|
23146
|
+
const [added2 = "0", removed = "0", ...path] = line2.split(" ");
|
|
23147
23147
|
return { path: path.join(" "), added: Number(added2) || 0, removed: Number(removed) || 0 };
|
|
23148
23148
|
});
|
|
23149
23149
|
const added = lines((await this.git(["ls-files", "--others", "--exclude-standard", "-z"])).out.replace(/\0/g, "\n"));
|
|
@@ -23189,8 +23189,8 @@ var Project = class {
|
|
|
23189
23189
|
async markedLines(path) {
|
|
23190
23190
|
try {
|
|
23191
23191
|
const all = (await readFile(join(this.copy, path), "utf8")).split("\n");
|
|
23192
|
-
const start = all.findIndex((
|
|
23193
|
-
const end2 = all.findIndex((
|
|
23192
|
+
const start = all.findIndex((line2) => line2.startsWith("<<<<<<< "));
|
|
23193
|
+
const end2 = all.findIndex((line2, at) => at > start && line2.startsWith(">>>>>>> "));
|
|
23194
23194
|
if (start < 0 || end2 < 0)
|
|
23195
23195
|
return void 0;
|
|
23196
23196
|
const shown2 = all.slice(Math.max(0, start - 1), end2 + 2);
|
|
@@ -23459,8 +23459,8 @@ ${sent.err}`;
|
|
|
23459
23459
|
env[`GIT_CONFIG_KEY_${at}`] = key;
|
|
23460
23460
|
env[`GIT_CONFIG_VALUE_${at}`] = value;
|
|
23461
23461
|
});
|
|
23462
|
-
return new Promise((
|
|
23463
|
-
execFile(options.git ?? "git", [...args], { cwd: this.copy, env, maxBuffer: 64 * 1024 * 1024 }, (error62, stdout, stderr) =>
|
|
23462
|
+
return new Promise((resolve7) => {
|
|
23463
|
+
execFile(options.git ?? "git", [...args], { cwd: this.copy, env, maxBuffer: 64 * 1024 * 1024 }, (error62, stdout, stderr) => resolve7({ ok: error62 === null, out: stdout, err: stderr }));
|
|
23464
23464
|
});
|
|
23465
23465
|
}
|
|
23466
23466
|
};
|
|
@@ -23483,8 +23483,8 @@ var Lane = class {
|
|
|
23483
23483
|
constructor(id2, wire) {
|
|
23484
23484
|
this.id = id2;
|
|
23485
23485
|
this.wire = wire;
|
|
23486
|
-
this.result = new Promise((
|
|
23487
|
-
this.settle =
|
|
23486
|
+
this.result = new Promise((resolve7) => {
|
|
23487
|
+
this.settle = resolve7;
|
|
23488
23488
|
});
|
|
23489
23489
|
this.stop = wire.hear(id2, (event) => this.onUpdated(event));
|
|
23490
23490
|
}
|
|
@@ -23627,7 +23627,7 @@ var Run = class {
|
|
|
23627
23627
|
/** Resolve once the run is not paused. A stopped run resolves too: check `over` after. */
|
|
23628
23628
|
async whenRunning() {
|
|
23629
23629
|
while (this.held && !this.ended) {
|
|
23630
|
-
await new Promise((
|
|
23630
|
+
await new Promise((resolve7) => setTimeout(resolve7, 50));
|
|
23631
23631
|
}
|
|
23632
23632
|
}
|
|
23633
23633
|
status(input2) {
|
|
@@ -24411,8 +24411,8 @@ var Chips = class _Chips {
|
|
|
24411
24411
|
}
|
|
24412
24412
|
/** Resolve with the first event that matches, now or later. */
|
|
24413
24413
|
waitFor(match) {
|
|
24414
|
-
return new Promise((
|
|
24415
|
-
this.waiters.add({ match, resolve:
|
|
24414
|
+
return new Promise((resolve7) => {
|
|
24415
|
+
this.waiters.add({ match, resolve: resolve7 });
|
|
24416
24416
|
});
|
|
24417
24417
|
}
|
|
24418
24418
|
close() {
|
|
@@ -24439,7 +24439,7 @@ var Chips = class _Chips {
|
|
|
24439
24439
|
return `${base}/v1/ws?ticket=${encodeURIComponent(String(ticket))}`;
|
|
24440
24440
|
}
|
|
24441
24441
|
open(first) {
|
|
24442
|
-
return new Promise((
|
|
24442
|
+
return new Promise((resolve7, reject) => {
|
|
24443
24443
|
this.setState("connecting");
|
|
24444
24444
|
const failed = (what, error62) => {
|
|
24445
24445
|
if (first)
|
|
@@ -24457,13 +24457,13 @@ var Chips = class _Chips {
|
|
|
24457
24457
|
}
|
|
24458
24458
|
this.socket = socket;
|
|
24459
24459
|
this.listen(socket, {
|
|
24460
|
-
settled: (hello2) => this.settled(socket, hello2, first,
|
|
24460
|
+
settled: (hello2) => this.settled(socket, hello2, first, resolve7, reject)
|
|
24461
24461
|
});
|
|
24462
24462
|
}, (error62) => failed("could not get a socket ticket", error62));
|
|
24463
24463
|
});
|
|
24464
24464
|
}
|
|
24465
24465
|
/** The handshake's outcome: the hello, a refusal, or the socket gone before either. */
|
|
24466
|
-
settled(socket, hello2, first,
|
|
24466
|
+
settled(socket, hello2, first, resolve7, reject) {
|
|
24467
24467
|
if (hello2 === void 0) {
|
|
24468
24468
|
if (first)
|
|
24469
24469
|
reject(new ConnectError("the server closed the socket before saying hello"));
|
|
@@ -24477,7 +24477,7 @@ var Chips = class _Chips {
|
|
|
24477
24477
|
return;
|
|
24478
24478
|
}
|
|
24479
24479
|
this.onHello(socket, hello2);
|
|
24480
|
-
void this.subscribed(socket).then(
|
|
24480
|
+
void this.subscribed(socket).then(resolve7);
|
|
24481
24481
|
}
|
|
24482
24482
|
/** The socket's four events. `settled` fires once: with the hello, a refusal, or nothing. */
|
|
24483
24483
|
listen(socket, on) {
|
|
@@ -24572,15 +24572,15 @@ var Chips = class _Chips {
|
|
|
24572
24572
|
subscribed(socket) {
|
|
24573
24573
|
this.pings += 1;
|
|
24574
24574
|
const ts = Date.now() + this.pings;
|
|
24575
|
-
return new Promise((
|
|
24575
|
+
return new Promise((resolve7) => {
|
|
24576
24576
|
const timer = setTimeout(() => {
|
|
24577
24577
|
this.pongs.delete(ts);
|
|
24578
|
-
|
|
24578
|
+
resolve7();
|
|
24579
24579
|
}, 2e3);
|
|
24580
24580
|
this.pongs.set(ts, () => {
|
|
24581
24581
|
clearTimeout(timer);
|
|
24582
24582
|
this.pongs.delete(ts);
|
|
24583
|
-
|
|
24583
|
+
resolve7();
|
|
24584
24584
|
});
|
|
24585
24585
|
socket.send(JSON.stringify({ frame: "ping", ts }));
|
|
24586
24586
|
});
|
|
@@ -24963,7 +24963,7 @@ function openKeyStore(env, platform = process.platform) {
|
|
|
24963
24963
|
// node_modules/@chipschat/harness/dist/steps.js
|
|
24964
24964
|
import { writeSync } from "node:fs";
|
|
24965
24965
|
var STEP_MARK = "chips-step ";
|
|
24966
|
-
function reportStep(step, detail = {}, env = process.env, write = (
|
|
24966
|
+
function reportStep(step, detail = {}, env = process.env, write = (line2) => void writeSync(1, line2)) {
|
|
24967
24967
|
if (env["CHIPS_STEPS"] !== "json")
|
|
24968
24968
|
return;
|
|
24969
24969
|
try {
|
|
@@ -25044,8 +25044,8 @@ async function refused(response) {
|
|
|
25044
25044
|
const body = await response.json().catch(() => ({}));
|
|
25045
25045
|
return new JoinError(body.error?.refusal ?? body.error?.code ?? `http_${response.status}`, body.error?.message ?? `Chips answered ${response.status}.`);
|
|
25046
25046
|
}
|
|
25047
|
-
var wait = (ms, signal) => new Promise((
|
|
25048
|
-
const timer = setTimeout(
|
|
25047
|
+
var wait = (ms, signal) => new Promise((resolve7, reject) => {
|
|
25048
|
+
const timer = setTimeout(resolve7, ms);
|
|
25049
25049
|
signal?.addEventListener("abort", () => {
|
|
25050
25050
|
clearTimeout(timer);
|
|
25051
25051
|
reject(new JoinError("stopped", "Stopped before a person decided."));
|
|
@@ -25169,8 +25169,8 @@ var MOST_FILES = 20;
|
|
|
25169
25169
|
var MOST_FILE_BYTES = 100 * 1024 * 1024;
|
|
25170
25170
|
function filesNamedIn(answer) {
|
|
25171
25171
|
const named = [];
|
|
25172
|
-
const kept = answer.split("\n").filter((
|
|
25173
|
-
const media = /^\s*MEDIA:\s*(\S.*?)\s*$/.exec(
|
|
25172
|
+
const kept = answer.split("\n").filter((line2) => {
|
|
25173
|
+
const media = /^\s*MEDIA:\s*(\S.*?)\s*$/.exec(line2);
|
|
25174
25174
|
if (media?.[1] === void 0)
|
|
25175
25175
|
return true;
|
|
25176
25176
|
if (!named.includes(media[1]))
|
|
@@ -25596,8 +25596,8 @@ function envValue(home2, name2) {
|
|
|
25596
25596
|
const path = join5(home2, ".env");
|
|
25597
25597
|
if (!existsSync5(path)) return void 0;
|
|
25598
25598
|
let found;
|
|
25599
|
-
for (const
|
|
25600
|
-
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/.exec(
|
|
25599
|
+
for (const line2 of readFileSync5(path, "utf8").split("\n")) {
|
|
25600
|
+
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/.exec(line2);
|
|
25601
25601
|
if (match?.[1] !== name2) continue;
|
|
25602
25602
|
const raw = match[2] ?? "";
|
|
25603
25603
|
const quoted2 = /^(["'])(.*)\1/.exec(raw);
|
|
@@ -25613,20 +25613,20 @@ function configText(home2) {
|
|
|
25613
25613
|
return "";
|
|
25614
25614
|
}
|
|
25615
25615
|
}
|
|
25616
|
-
function uncommented(
|
|
25617
|
-
const indent =
|
|
25616
|
+
function uncommented(line2) {
|
|
25617
|
+
const indent = line2.length - line2.trimStart().length;
|
|
25618
25618
|
let quote;
|
|
25619
|
-
for (let at = indent; at <
|
|
25620
|
-
const char =
|
|
25619
|
+
for (let at = indent; at < line2.length; at += 1) {
|
|
25620
|
+
const char = line2[at];
|
|
25621
25621
|
if (quote !== void 0) {
|
|
25622
25622
|
if (char === quote) quote = void 0;
|
|
25623
25623
|
} else if (char === '"' || char === "'") {
|
|
25624
25624
|
quote = char;
|
|
25625
|
-
} else if (char === "#" && (at === indent || /\s/.test(
|
|
25626
|
-
return { indent, text:
|
|
25625
|
+
} else if (char === "#" && (at === indent || /\s/.test(line2[at - 1] ?? ""))) {
|
|
25626
|
+
return { indent, text: line2.slice(indent, at).trimEnd() };
|
|
25627
25627
|
}
|
|
25628
25628
|
}
|
|
25629
|
-
return { indent, text:
|
|
25629
|
+
return { indent, text: line2.slice(indent).trimEnd() };
|
|
25630
25630
|
}
|
|
25631
25631
|
function scalar(text) {
|
|
25632
25632
|
const trimmed = text.trim();
|
|
@@ -25648,9 +25648,9 @@ function flowPairs(text) {
|
|
|
25648
25648
|
function yamlValue(text, section, key) {
|
|
25649
25649
|
const lines2 = text.split("\n");
|
|
25650
25650
|
for (let at = 0; at < lines2.length; at += 1) {
|
|
25651
|
-
const { indent, text:
|
|
25652
|
-
if (indent !== 0 || !
|
|
25653
|
-
const rest =
|
|
25651
|
+
const { indent, text: line2 } = uncommented(lines2[at] ?? "");
|
|
25652
|
+
if (indent !== 0 || !line2.startsWith(`${section}:`)) continue;
|
|
25653
|
+
const rest = line2.slice(section.length + 1).trim();
|
|
25654
25654
|
if (rest !== "") return flowPairs(rest)?.get(key);
|
|
25655
25655
|
let child;
|
|
25656
25656
|
for (let below = at + 1; below < lines2.length; below += 1) {
|
|
@@ -25722,7 +25722,9 @@ function folderOfPid(raw) {
|
|
|
25722
25722
|
} catch {
|
|
25723
25723
|
return void 0;
|
|
25724
25724
|
}
|
|
25725
|
-
|
|
25725
|
+
return pid === void 0 ? void 0 : processFolder(pid);
|
|
25726
|
+
}
|
|
25727
|
+
function processFolder(pid) {
|
|
25726
25728
|
try {
|
|
25727
25729
|
if (process.platform === "linux") return readlinkSync(`/proc/${pid}/cwd`);
|
|
25728
25730
|
const out = execFileSync3("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], {
|
|
@@ -25730,8 +25732,8 @@ function folderOfPid(raw) {
|
|
|
25730
25732
|
timeout: 3e3,
|
|
25731
25733
|
stdio: ["ignore", "pipe", "ignore"]
|
|
25732
25734
|
});
|
|
25733
|
-
const
|
|
25734
|
-
return
|
|
25735
|
+
const line2 = out.split("\n").find((each) => each.startsWith("n"));
|
|
25736
|
+
return line2 === void 0 ? void 0 : line2.slice(1);
|
|
25735
25737
|
} catch {
|
|
25736
25738
|
return void 0;
|
|
25737
25739
|
}
|
|
@@ -26529,7 +26531,7 @@ function sessionIdOf(made) {
|
|
|
26529
26531
|
return typeof id2 === "string" && id2 !== "" ? id2 : void 0;
|
|
26530
26532
|
}
|
|
26531
26533
|
function eventOf(block) {
|
|
26532
|
-
const data = block.split("\n").filter((
|
|
26534
|
+
const data = block.split("\n").filter((line2) => line2.startsWith("data:")).map((line2) => line2.slice(5).replace(/^ /, "")).join("\n");
|
|
26533
26535
|
if (data === "") return void 0;
|
|
26534
26536
|
try {
|
|
26535
26537
|
const parsed = JSON.parse(data);
|
|
@@ -26564,9 +26566,9 @@ function hookUrl(port, profile) {
|
|
|
26564
26566
|
return `http://127.0.0.1:${port}${HOOK_PATH}${profile}`;
|
|
26565
26567
|
}
|
|
26566
26568
|
function linesOf2(text) {
|
|
26567
|
-
return text.split("\n").map((
|
|
26568
|
-
const indent =
|
|
26569
|
-
const words4 =
|
|
26569
|
+
return text.split("\n").map((line2) => {
|
|
26570
|
+
const indent = line2.length - line2.trimStart().length;
|
|
26571
|
+
const words4 = line2.trimStart().startsWith("#") ? "" : line2.trim().replace(/\s+#.*$/, "");
|
|
26570
26572
|
return { indent, words: words4 };
|
|
26571
26573
|
});
|
|
26572
26574
|
}
|
|
@@ -26608,15 +26610,15 @@ function addHook(home2, port, profile, secret) {
|
|
|
26608
26610
|
const text = existsSync7(path) ? readFileSync7(path, "utf8") : "";
|
|
26609
26611
|
const lines2 = linesOf2(text);
|
|
26610
26612
|
const ours2 = new RegExp(`^(?:-\\s*)?url:\\s*["']?${url2.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&")}["']?$`);
|
|
26611
|
-
if (lines2.some((
|
|
26612
|
-
const where = lines2.findIndex((
|
|
26613
|
+
if (lines2.some((line2) => ours2.test(line2.words))) return { state: "there", path };
|
|
26614
|
+
const where = lines2.findIndex((line2) => line2.indent === 0 && /^["']?hooks["']?\s*:/.test(line2.words));
|
|
26613
26615
|
const byHand = (why) => {
|
|
26614
26616
|
const inEnv2 = secretInEnv(home2, secret);
|
|
26615
26617
|
const shown2 = entryLines(" ", url2, inEnv2 ? { env: SECRET_ENV } : { inline: secret });
|
|
26616
26618
|
return { state: "by_hand", path, why, lines: ["hooks:", ...shown2].join("\n") };
|
|
26617
26619
|
};
|
|
26618
26620
|
const plain = /^(?:"[^"]+"|'[^']+'|[A-Za-z_][\w.-]*)\s*:(?:\s|$)/;
|
|
26619
|
-
if (lines2.some((
|
|
26621
|
+
if (lines2.some((line2) => line2.indent === 0 && line2.words !== "" && !plain.test(line2.words))) {
|
|
26620
26622
|
return byHand("its top level isn't laid out as this bridge expects");
|
|
26621
26623
|
}
|
|
26622
26624
|
if (where === -1) {
|
|
@@ -26636,20 +26638,20 @@ function addHook(home2, port, profile, secret) {
|
|
|
26636
26638
|
const head = lines2[where]?.words ?? "";
|
|
26637
26639
|
if (head.replace(/^["']?hooks["']?\s*:/, "").trim() !== "") return byHand("its hooks: is written on one line");
|
|
26638
26640
|
const children = [];
|
|
26639
|
-
for (const
|
|
26640
|
-
if (
|
|
26641
|
-
if (
|
|
26642
|
-
children.push(
|
|
26641
|
+
for (const line2 of lines2.slice(where + 1)) {
|
|
26642
|
+
if (line2.words === "") continue;
|
|
26643
|
+
if (line2.indent === 0) break;
|
|
26644
|
+
children.push(line2);
|
|
26643
26645
|
}
|
|
26644
26646
|
const indent = children[0]?.indent ?? 2;
|
|
26645
26647
|
const named = new RegExp(`^(?:-\\s*)?name:\\s*["']?${HOOK_NAME}["']?$`);
|
|
26646
|
-
if (children.some((
|
|
26648
|
+
if (children.some((line2) => named.test(line2.words))) {
|
|
26647
26649
|
return byHand(`its hooks have an entry called ${HOOK_NAME} with another address`);
|
|
26648
26650
|
}
|
|
26649
|
-
if (children.some((
|
|
26651
|
+
if (children.some((line2) => line2.indent === indent && /^["']?outbound["']?\s*:/.test(line2.words))) {
|
|
26650
26652
|
return byHand("its hooks already have an outbound list");
|
|
26651
26653
|
}
|
|
26652
|
-
if (children.some((
|
|
26654
|
+
if (children.some((line2) => line2.indent !== indent && line2.indent < indent) || children[0]?.words.startsWith("-")) {
|
|
26653
26655
|
return byHand("its hooks: isn't laid out as this bridge expects");
|
|
26654
26656
|
}
|
|
26655
26657
|
const inEnv = secretInEnv(home2, secret);
|
|
@@ -26660,7 +26662,7 @@ function addHook(home2, port, profile, secret) {
|
|
|
26660
26662
|
}
|
|
26661
26663
|
|
|
26662
26664
|
// src/bridge.ts
|
|
26663
|
-
var WANTS = ["agents", "new_agents", "forks", "context_files", "context_windows"];
|
|
26665
|
+
var WANTS = ["agents", "new_agents", "forks", "context_files", "context_windows", "restarts"];
|
|
26664
26666
|
function settingsFor(active) {
|
|
26665
26667
|
const bridge2 = () => {
|
|
26666
26668
|
const there = active();
|
|
@@ -26725,7 +26727,7 @@ var Bridge = class {
|
|
|
26725
26727
|
agent: (agentId2, copy) => chips2.settings.context(agentId2, copy),
|
|
26726
26728
|
harness: (copy) => chips2.settings.harnessContext(copy)
|
|
26727
26729
|
},
|
|
26728
|
-
(
|
|
26730
|
+
(line2) => this.log(`context: ${line2}`),
|
|
26729
26731
|
options.env ?? process.env
|
|
26730
26732
|
);
|
|
26731
26733
|
if (options.fills === void 0) return;
|
|
@@ -26740,7 +26742,7 @@ var Bridge = class {
|
|
|
26740
26742
|
send: (agentId2, copy) => chips2.settings.contextWindow(agentId2, copy),
|
|
26741
26743
|
kept,
|
|
26742
26744
|
save: () => options.saveState(options.state),
|
|
26743
|
-
log: (
|
|
26745
|
+
log: (line2) => this.log(`context window: ${line2}`),
|
|
26744
26746
|
sendEveryMs: options.fillsEveryMs
|
|
26745
26747
|
});
|
|
26746
26748
|
}
|
|
@@ -26767,6 +26769,10 @@ var Bridge = class {
|
|
|
26767
26769
|
/** The port Hermes's webhook reaches the bridge on, once it listens. */
|
|
26768
26770
|
fillsPort;
|
|
26769
26771
|
toldNoReadings = false;
|
|
26772
|
+
/** While Hermes's gateway restarts (Chips ADR 0094), the health watch says nothing: the restart says how it went. */
|
|
26773
|
+
held = false;
|
|
26774
|
+
/** What went wrong in the last restart, said in place of the watch's own words while Hermes doesn't answer. */
|
|
26775
|
+
why;
|
|
26770
26776
|
/** Register the agents, start hearing what is said to them, and say how their gateways are. */
|
|
26771
26777
|
async start() {
|
|
26772
26778
|
const { chips: chips2 } = this.options;
|
|
@@ -26795,6 +26801,20 @@ var Bridge = class {
|
|
|
26795
26801
|
this.health.unref();
|
|
26796
26802
|
await this.startSettings();
|
|
26797
26803
|
}
|
|
26804
|
+
/** Hold the health watch's words while Hermes's gateway restarts, and let them go after. */
|
|
26805
|
+
hold(held) {
|
|
26806
|
+
this.held = held;
|
|
26807
|
+
}
|
|
26808
|
+
/**
|
|
26809
|
+
* How a restart went (Chips ADR 0094): fine as the gateways now are, or its words, which stand
|
|
26810
|
+
* while Hermes doesn't answer. The health watch speaks for itself again after.
|
|
26811
|
+
*/
|
|
26812
|
+
async restarted(trouble) {
|
|
26813
|
+
this.held = false;
|
|
26814
|
+
this.why = trouble;
|
|
26815
|
+
if (trouble === void 0) return this.sayHowItIs();
|
|
26816
|
+
await this.options.chips.reportTrouble(trouble).catch(() => void 0);
|
|
26817
|
+
}
|
|
26798
26818
|
stop() {
|
|
26799
26819
|
if (this.health !== void 0) clearInterval(this.health);
|
|
26800
26820
|
if (this.contextTimer !== void 0) clearInterval(this.contextTimer);
|
|
@@ -27034,7 +27054,7 @@ var Bridge = class {
|
|
|
27034
27054
|
const folder2 = turn.folder;
|
|
27035
27055
|
if (folder2 === void 0) return void 0;
|
|
27036
27056
|
try {
|
|
27037
|
-
const project = this.options.chips.project({ spaceId, agentId: agentId2, name: name2, folder: folder2, log: (
|
|
27057
|
+
const project = this.options.chips.project({ spaceId, agentId: agentId2, name: name2, folder: folder2, log: (line2) => this.log(line2) });
|
|
27038
27058
|
const facts = await project.pull();
|
|
27039
27059
|
turn.project = { copy: project, name: name2 };
|
|
27040
27060
|
return facts;
|
|
@@ -27063,12 +27083,15 @@ var Bridge = class {
|
|
|
27063
27083
|
return note === void 0 ? {} : { note };
|
|
27064
27084
|
}
|
|
27065
27085
|
}
|
|
27066
|
-
/** Trouble while a profile's gateway doesn't answer, and fine once all do (ADR 0083). */
|
|
27086
|
+
/** Trouble while a profile's gateway doesn't answer, and fine once all do (ADR 0083); nothing while a restart runs. */
|
|
27067
27087
|
async sayHowItIs() {
|
|
27088
|
+
if (this.held) return;
|
|
27068
27089
|
for (const agent of this.agents) agent.up = await agent.client.healthy();
|
|
27090
|
+
if (this.held) return;
|
|
27069
27091
|
const down = this.agents.filter((agent) => !agent.up);
|
|
27092
|
+
if (down.length === 0) this.why = void 0;
|
|
27070
27093
|
const chips2 = this.options.chips;
|
|
27071
|
-
const said2 = down.length === 0 ? chips2.reportOk() : chips2.reportTrouble(`Hermes isn't answering for ${down.map((agent) => agent.config.name).join(", ")}`);
|
|
27094
|
+
const said2 = down.length === 0 ? chips2.reportOk() : chips2.reportTrouble(this.why ?? `Hermes isn't answering for ${down.map((agent) => agent.config.name).join(", ")}`);
|
|
27072
27095
|
await said2.catch(() => void 0);
|
|
27073
27096
|
}
|
|
27074
27097
|
// ------------------------------------------------------------------ what is said to it
|
|
@@ -27102,15 +27125,15 @@ var Bridge = class {
|
|
|
27102
27125
|
this.lookedAt.delete(old);
|
|
27103
27126
|
}
|
|
27104
27127
|
const context = await this.options.chips.roomContext(spaceId, { limit: 200 }).catch(() => void 0);
|
|
27105
|
-
const
|
|
27128
|
+
const line2 = context?.lines.find((each) => each.message_id === messageId);
|
|
27106
27129
|
const message = {
|
|
27107
27130
|
type: "message.new",
|
|
27108
27131
|
space_id: spaceId,
|
|
27109
27132
|
// An id of its own, so nothing of the turn is taken for the reply's.
|
|
27110
27133
|
message_id: messageIdOf(feedback2.id),
|
|
27111
|
-
text: lookAgain(feedback2.from.display_name,
|
|
27134
|
+
text: lookAgain(feedback2.from.display_name, line2?.text, note),
|
|
27112
27135
|
from: feedback2.from,
|
|
27113
|
-
...
|
|
27136
|
+
...line2 === void 0 ? {} : { for: [line2.from.id] }
|
|
27114
27137
|
};
|
|
27115
27138
|
await this.hear(message, { goal: lookingAgain(note), sayWho: false });
|
|
27116
27139
|
}
|
|
@@ -27252,7 +27275,7 @@ var Bridge = class {
|
|
|
27252
27275
|
if (status !== void 0 && TERMINAL.has(`run.${status}`)) {
|
|
27253
27276
|
return { ...state2, event: `run.${status}` };
|
|
27254
27277
|
}
|
|
27255
|
-
await new Promise((
|
|
27278
|
+
await new Promise((resolve7) => setTimeout(resolve7, 1e3));
|
|
27256
27279
|
}
|
|
27257
27280
|
await this.failed(turn, `${turn.agent.config.name}'s run in Hermes stopped answering.`, true);
|
|
27258
27281
|
return void 0;
|
|
@@ -27320,10 +27343,10 @@ var Bridge = class {
|
|
|
27320
27343
|
}
|
|
27321
27344
|
sayThought(turn, text, phase) {
|
|
27322
27345
|
const now = Date.now();
|
|
27323
|
-
const
|
|
27324
|
-
if (
|
|
27346
|
+
const line2 = lastLine(text);
|
|
27347
|
+
if (line2 === "" || now - turn.saidAt < QUIET_MS) return;
|
|
27325
27348
|
turn.saidAt = now;
|
|
27326
|
-
void turn.run.status({ phase, verbPhrase:
|
|
27349
|
+
void turn.run.status({ phase, verbPhrase: line2.slice(0, 200) }).catch(() => void 0);
|
|
27327
27350
|
}
|
|
27328
27351
|
async started(turn, event) {
|
|
27329
27352
|
const sub = event.event === "subagent.start";
|
|
@@ -27392,7 +27415,7 @@ var Bridge = class {
|
|
|
27392
27415
|
${command}
|
|
27393
27416
|
\`\`\`` } }
|
|
27394
27417
|
});
|
|
27395
|
-
const stopped2 = new Promise((
|
|
27418
|
+
const stopped2 = new Promise((resolve7) => turn.run.on("cancel", () => resolve7(void 0)));
|
|
27396
27419
|
const decision = await Promise.race([asked, stopped2]);
|
|
27397
27420
|
const pending = turn.approvals.get(requestId ?? approvalId);
|
|
27398
27421
|
if (decision === void 0 || pending === void 0 || pending.answered) return;
|
|
@@ -27400,7 +27423,7 @@ ${command}
|
|
|
27400
27423
|
if (decision.allowed) turn.allowed.push(approvalId);
|
|
27401
27424
|
const generation = event["execution_generation"];
|
|
27402
27425
|
await turn.agent.client.approve(turn.hermesRun, {
|
|
27403
|
-
choice: choiceOf(decision, choices),
|
|
27426
|
+
choice: choiceOf(decision, choices, command !== void 0),
|
|
27404
27427
|
...requestId === void 0 ? {} : { request_id: requestId },
|
|
27405
27428
|
...typeof generation === "number" ? { execution_generation: generation } : {}
|
|
27406
27429
|
}).catch((error62) => this.log(`Hermes didn't take the answer: ${messageOf2(error62)}`));
|
|
@@ -27517,8 +27540,9 @@ ${command}
|
|
|
27517
27540
|
if (retryable) void this.sayHowItIs();
|
|
27518
27541
|
}
|
|
27519
27542
|
};
|
|
27520
|
-
function choiceOf(decision, choices) {
|
|
27543
|
+
function choiceOf(decision, choices, command = false) {
|
|
27521
27544
|
if (!decision.allowed) return "deny";
|
|
27545
|
+
if (command) return "once";
|
|
27522
27546
|
if (decision.scope === "always" && choices.includes("always")) return "always";
|
|
27523
27547
|
if (decision.scope !== "once" && choices.includes("session")) return "session";
|
|
27524
27548
|
return "once";
|
|
@@ -27535,7 +27559,7 @@ function forkName(original) {
|
|
|
27535
27559
|
return `${original.trim().replace(/ · fork( \d+)?$/, "")} \xB7 fork`;
|
|
27536
27560
|
}
|
|
27537
27561
|
function goalOf(message, name2) {
|
|
27538
|
-
const first = message.text.split("\n").find((
|
|
27562
|
+
const first = message.text.split("\n").find((line2) => line2.trim() !== "")?.trim() ?? "";
|
|
27539
27563
|
const named = first.toLowerCase().startsWith(`@${name2.toLowerCase()}`);
|
|
27540
27564
|
const goal = named ? first.slice(name2.length + 1).trim() : first.replace(/^@\S+\s*/, "");
|
|
27541
27565
|
return (goal === "" ? `Answer ${message.from.display_name}` : goal).slice(0, 300);
|
|
@@ -27553,7 +27577,7 @@ function lookAgain(who, reply, note) {
|
|
|
27553
27577
|
].join("\n\n");
|
|
27554
27578
|
}
|
|
27555
27579
|
function lookingAgain(note) {
|
|
27556
|
-
const first = note?.split("\n").find((
|
|
27580
|
+
const first = note?.split("\n").find((line2) => line2.trim() !== "")?.trim();
|
|
27557
27581
|
return first === void 0 ? "Looking again at its reply" : cut(`Looking again: ${first}`, GOAL_SHOWN);
|
|
27558
27582
|
}
|
|
27559
27583
|
function cut(text, most) {
|
|
@@ -27573,27 +27597,488 @@ function resultWords(preview2) {
|
|
|
27573
27597
|
return typeof said2 === "string" ? said2.trim() : "";
|
|
27574
27598
|
}
|
|
27575
27599
|
function lastLine(text) {
|
|
27576
|
-
const lines2 = text.split(/(?<=[.!?])\s+|\n+/).map((
|
|
27600
|
+
const lines2 = text.split(/(?<=[.!?])\s+|\n+/).map((line2) => line2.trim()).filter((line2) => line2 !== "");
|
|
27577
27601
|
return lines2.at(-1) ?? "";
|
|
27578
27602
|
}
|
|
27579
27603
|
function lowerFirst(text) {
|
|
27580
27604
|
return text.length === 0 ? text : `${text[0]?.toLowerCase() ?? ""}${text.slice(1)}`;
|
|
27581
27605
|
}
|
|
27582
27606
|
|
|
27583
|
-
// src/
|
|
27607
|
+
// src/gateway.ts
|
|
27608
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
27609
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
27610
|
+
import { accessSync as accessSync2, appendFileSync as appendFileSync2, closeSync as closeSync2, constants as constants2, existsSync as existsSync8, openSync as openSync2, readFileSync as readFileSync8, readSync, statSync as statSync4 } from "node:fs";
|
|
27584
27611
|
import { homedir as homedir5 } from "node:os";
|
|
27585
|
-
import { basename as basename4, join as join9, resolve as resolve3 } from "node:path";
|
|
27612
|
+
import { basename as basename4, delimiter as delimiter2, dirname as dirname5, join as join9, resolve as resolve3 } from "node:path";
|
|
27613
|
+
var TIMING = {
|
|
27614
|
+
stopMs: 15e3,
|
|
27615
|
+
killMs: 5e3,
|
|
27616
|
+
earlyMs: 1e4,
|
|
27617
|
+
upMs: 9e4,
|
|
27618
|
+
serviceMs: 15e4,
|
|
27619
|
+
pollMs: 500
|
|
27620
|
+
};
|
|
27621
|
+
var EXPAT = "/opt/homebrew/opt/expat/lib/libexpat.1.dylib";
|
|
27622
|
+
var EXPAT_SYMBOL = "_XML_SetAllocTrackerActivationThreshold";
|
|
27623
|
+
function gatewaysOf(agents) {
|
|
27624
|
+
return [...new Set(agents.map(({ address }) => address.url.replace(/\/+$/, "")))].map((url2) => ({ url: url2 }));
|
|
27625
|
+
}
|
|
27626
|
+
function isLocal(url2) {
|
|
27627
|
+
try {
|
|
27628
|
+
return ["127.0.0.1", "localhost", "[::1]", "::1"].includes(new URL(url2).hostname);
|
|
27629
|
+
} catch {
|
|
27630
|
+
return false;
|
|
27631
|
+
}
|
|
27632
|
+
}
|
|
27633
|
+
function portOf(url2) {
|
|
27634
|
+
const parsed = new URL(url2);
|
|
27635
|
+
return Number(parsed.port || (parsed.protocol === "https:" ? 443 : 80));
|
|
27636
|
+
}
|
|
27637
|
+
function line(words4) {
|
|
27638
|
+
const one = words4.replace(/\s+/g, " ").trim();
|
|
27639
|
+
return one.length <= 200 ? one : `${one.slice(0, 199)}\u2026`;
|
|
27640
|
+
}
|
|
27641
|
+
function whyFrom(output2, code) {
|
|
27642
|
+
const lines2 = output2.split("\n").map((each) => each.trim()).filter((each) => each !== "");
|
|
27643
|
+
const why = lines2.findLast((each) => /^[A-Za-z_][\w.]*(Error|Exception)\b/.test(each)) ?? lines2.at(-1);
|
|
27644
|
+
if (why === void 0) return code === null || code === void 0 ? "it was stopped" : `it exited ${code}`;
|
|
27645
|
+
return why.length <= 200 ? why : why.replace(/\/(?:[^\s/(),:'"]+\/)+([^\s/(),:'"]+)/g, "$1");
|
|
27646
|
+
}
|
|
27647
|
+
function gatewayRunArgs(command) {
|
|
27648
|
+
const words4 = command.trim().split(/\s+/);
|
|
27649
|
+
const at = words4.findIndex((word) => basename4(word) === "hermes");
|
|
27650
|
+
if (at === -1) return void 0;
|
|
27651
|
+
const args = words4.slice(at + 1);
|
|
27652
|
+
const plain = [];
|
|
27653
|
+
for (let next = 0; next < args.length; next += 1) {
|
|
27654
|
+
const word = args[next] ?? "";
|
|
27655
|
+
if (word === "-p" || word === "--profile") next += 1;
|
|
27656
|
+
else if (!word.startsWith("-")) plain.push(word);
|
|
27657
|
+
}
|
|
27658
|
+
return plain[0] === "gateway" && plain[1] === "run" ? args : void 0;
|
|
27659
|
+
}
|
|
27660
|
+
function serviceOf(system, root2, env = {}) {
|
|
27661
|
+
const home2 = resolve3(root2);
|
|
27662
|
+
const native = resolve3(system.home, ".hermes");
|
|
27663
|
+
const suffix = home2 === native ? "" : dirname5(home2) === join9(native, "profiles") ? basename4(home2) : createHash3("sha256").update(home2).digest("hex").slice(0, 8);
|
|
27664
|
+
if (system.platform === "darwin") {
|
|
27665
|
+
const path = join9(system.home, "Library", "LaunchAgents", `ai.hermes.gateway${suffix ? `-${suffix}` : ""}.plist`);
|
|
27666
|
+
return system.exists(path) ? { kind: "launchd", path } : void 0;
|
|
27667
|
+
}
|
|
27668
|
+
if (system.platform === "linux") {
|
|
27669
|
+
const unit = `hermes-gateway${suffix ? `-${suffix}` : ""}.service`;
|
|
27670
|
+
const user = join9(env["XDG_CONFIG_HOME"] || join9(system.home, ".config"), "systemd", "user", unit);
|
|
27671
|
+
if (system.exists(user)) return { kind: "systemd", path: user };
|
|
27672
|
+
const machine = join9("/etc/systemd/system", unit);
|
|
27673
|
+
if (system.exists(machine)) return { kind: "systemd", path: machine, system: true };
|
|
27674
|
+
}
|
|
27675
|
+
return void 0;
|
|
27676
|
+
}
|
|
27677
|
+
function expand(text, env) {
|
|
27678
|
+
let out = "";
|
|
27679
|
+
let at = 0;
|
|
27680
|
+
while (at < text.length) {
|
|
27681
|
+
const char = text[at] ?? "";
|
|
27682
|
+
if (char === "\\" && at + 1 < text.length) {
|
|
27683
|
+
out += text[at + 1];
|
|
27684
|
+
at += 2;
|
|
27685
|
+
} else if (char !== "$") {
|
|
27686
|
+
out += char;
|
|
27687
|
+
at += 1;
|
|
27688
|
+
} else if (text[at + 1] === "{") {
|
|
27689
|
+
let depth = 1;
|
|
27690
|
+
let end2 = at + 2;
|
|
27691
|
+
for (; end2 < text.length; end2 += 1) {
|
|
27692
|
+
if (text[end2] === "{") depth += 1;
|
|
27693
|
+
else if (text[end2] === "}" && --depth === 0) break;
|
|
27694
|
+
}
|
|
27695
|
+
const inner = /^([A-Za-z_][A-Za-z0-9_]*)(?:(:?)-([\s\S]*))?$/.exec(text.slice(at + 2, end2));
|
|
27696
|
+
if (inner !== null) {
|
|
27697
|
+
const value = env[inner[1] ?? ""];
|
|
27698
|
+
const unset = inner[2] === ":" ? value === void 0 || value === "" : value === void 0;
|
|
27699
|
+
out += inner[3] !== void 0 && unset ? expand(inner[3], env) : value ?? "";
|
|
27700
|
+
}
|
|
27701
|
+
at = end2 + 1;
|
|
27702
|
+
} else {
|
|
27703
|
+
const name2 = /^[A-Za-z_][A-Za-z0-9_]*/.exec(text.slice(at + 1))?.[0];
|
|
27704
|
+
out += name2 === void 0 ? "$" : env[name2] ?? "";
|
|
27705
|
+
at += 1 + (name2?.length ?? 0);
|
|
27706
|
+
}
|
|
27707
|
+
}
|
|
27708
|
+
return out;
|
|
27709
|
+
}
|
|
27710
|
+
function wrapperOf(text, env) {
|
|
27711
|
+
for (const each of text.split("\n")) {
|
|
27712
|
+
const found = /^\s*((?:[A-Za-z_][A-Za-z0-9_]*="(?:[^"\\]|\\.)*"\s+)*)exec\s+"([^"]+)"/.exec(each);
|
|
27713
|
+
if (found === null) continue;
|
|
27714
|
+
const vars = {};
|
|
27715
|
+
for (const set2 of (found[1] ?? "").matchAll(/([A-Za-z_][A-Za-z0-9_]*)="((?:[^"\\]|\\.)*)"/g)) {
|
|
27716
|
+
vars[set2[1] ?? ""] = expand(set2[2] ?? "", env);
|
|
27717
|
+
}
|
|
27718
|
+
return { entry: expand(found[2] ?? "", env), vars };
|
|
27719
|
+
}
|
|
27720
|
+
return void 0;
|
|
27721
|
+
}
|
|
27722
|
+
var NO_HERMES = "The hermes command isn't on this machine's PATH, so Hermes's gateway can't be started.";
|
|
27723
|
+
var messageOf3 = (error62) => error62 instanceof Error ? error62.message : String(error62);
|
|
27724
|
+
function gatewayEnv(env) {
|
|
27725
|
+
return Object.fromEntries(Object.entries(env).filter(([name2]) => !name2.startsWith("CHIPS_")));
|
|
27726
|
+
}
|
|
27727
|
+
var Gateways = class {
|
|
27728
|
+
constructor(options) {
|
|
27729
|
+
this.options = options;
|
|
27730
|
+
}
|
|
27731
|
+
options;
|
|
27732
|
+
timing = TIMING;
|
|
27733
|
+
/** Each gateway restarted, one after another: what went wrong, in a line, or nothing. */
|
|
27734
|
+
async restart() {
|
|
27735
|
+
const troubles = [];
|
|
27736
|
+
for (const gateway of this.options.gateways) {
|
|
27737
|
+
let trouble;
|
|
27738
|
+
try {
|
|
27739
|
+
trouble = await this.restartOne(gateway);
|
|
27740
|
+
} catch (error62) {
|
|
27741
|
+
trouble = `Restart failed: ${messageOf3(error62)}`;
|
|
27742
|
+
}
|
|
27743
|
+
if (trouble !== void 0) troubles.push(trouble);
|
|
27744
|
+
}
|
|
27745
|
+
return troubles.length === 0 ? void 0 : line(troubles.join(" "));
|
|
27746
|
+
}
|
|
27747
|
+
/** Each local gateway that doesn't answer, started: the bridge is starting. */
|
|
27748
|
+
async startIfDown() {
|
|
27749
|
+
const { system, log: log2 } = this.options;
|
|
27750
|
+
for (const gateway of this.options.gateways) {
|
|
27751
|
+
if (!isLocal(gateway.url)) continue;
|
|
27752
|
+
const port = portOf(gateway.url);
|
|
27753
|
+
try {
|
|
27754
|
+
if (await system.healthy(gateway.url)) continue;
|
|
27755
|
+
let trouble;
|
|
27756
|
+
if (this.serviceFor(gateway) !== void 0) {
|
|
27757
|
+
log2(`Hermes isn't answering on port ${port}: starting its service, hermes gateway start`);
|
|
27758
|
+
trouble = await this.byService(gateway, "start", void 0);
|
|
27759
|
+
} else {
|
|
27760
|
+
const pid = await system.listener(port);
|
|
27761
|
+
if (pid !== void 0) {
|
|
27762
|
+
log2(`Hermes isn't answering on port ${port}, but pid ${pid} listens there: no second gateway started`);
|
|
27763
|
+
continue;
|
|
27764
|
+
}
|
|
27765
|
+
log2(`Hermes isn't answering on port ${port}: starting hermes gateway run`);
|
|
27766
|
+
trouble = await this.start(gateway, ["gateway", "run"], void 0);
|
|
27767
|
+
}
|
|
27768
|
+
if (trouble !== void 0) log2(trouble);
|
|
27769
|
+
} catch (error62) {
|
|
27770
|
+
log2(`Hermes's gateway on port ${port} couldn't be started: ${messageOf3(error62)}`);
|
|
27771
|
+
}
|
|
27772
|
+
}
|
|
27773
|
+
}
|
|
27774
|
+
/**
|
|
27775
|
+
* The service of Hermes's root, for the Main agent's gateway: a gateway at another address is
|
|
27776
|
+
* another profile's own, which `hermes gateway status` of the root doesn't speak for.
|
|
27777
|
+
*/
|
|
27778
|
+
serviceFor(gateway) {
|
|
27779
|
+
if (gateway !== this.options.gateways[0]) return void 0;
|
|
27780
|
+
return serviceOf(this.options.system, this.options.root, this.options.env);
|
|
27781
|
+
}
|
|
27782
|
+
async restartOne(gateway) {
|
|
27783
|
+
const { system, log: log2 } = this.options;
|
|
27784
|
+
if (!isLocal(gateway.url)) {
|
|
27785
|
+
return `Hermes's gateway at ${new URL(gateway.url).host} isn't on this machine, so it can't be restarted from here.`;
|
|
27786
|
+
}
|
|
27787
|
+
const port = portOf(gateway.url);
|
|
27788
|
+
const pid = await system.listener(port);
|
|
27789
|
+
if (this.serviceFor(gateway) !== void 0) {
|
|
27790
|
+
log2("restarting Hermes's service: hermes gateway restart");
|
|
27791
|
+
return this.byService(gateway, "restart", pid);
|
|
27792
|
+
}
|
|
27793
|
+
if (pid === void 0) {
|
|
27794
|
+
if (await system.healthy(gateway.url)) return `Can't tell what answers on port ${port}, so nothing was restarted.`;
|
|
27795
|
+
log2(`nothing listens on port ${port}: starting hermes gateway run`);
|
|
27796
|
+
return this.start(gateway, ["gateway", "run"], void 0);
|
|
27797
|
+
}
|
|
27798
|
+
const info = await system.describe(pid);
|
|
27799
|
+
if (info === void 0) return this.start(gateway, ["gateway", "run"], void 0);
|
|
27800
|
+
const args = this.ours(info);
|
|
27801
|
+
if (args === void 0) {
|
|
27802
|
+
log2(`pid ${pid} on port ${port} isn't this user's hermes gateway run, so it was left alone: ${info.command.slice(0, 160)}`);
|
|
27803
|
+
return `Port ${port} is held by something other than hermes gateway run (pid ${pid}), so nothing was restarted.`;
|
|
27804
|
+
}
|
|
27805
|
+
if (system.which("hermes", this.options.env) === void 0) return NO_HERMES;
|
|
27806
|
+
log2(`stopping Hermes's gateway, pid ${pid}`);
|
|
27807
|
+
if (!await this.stop(pid)) return `Hermes's gateway (pid ${pid}) didn't stop, so it wasn't started again.`;
|
|
27808
|
+
return this.start(gateway, args, info.cwd);
|
|
27809
|
+
}
|
|
27810
|
+
/** A `hermes gateway run` of this user's: the words to start it again with. */
|
|
27811
|
+
ours(info) {
|
|
27812
|
+
return info.uid === this.options.system.uid ? gatewayRunArgs(info.command) : void 0;
|
|
27813
|
+
}
|
|
27814
|
+
/** SIGTERM, then SIGKILL once it has had a while, and only while it is still that gateway. */
|
|
27815
|
+
async stop(pid) {
|
|
27816
|
+
const { system, log: log2 } = this.options;
|
|
27817
|
+
system.signal(pid, "SIGTERM");
|
|
27818
|
+
if (await this.gone(pid, this.timing.stopMs)) return true;
|
|
27819
|
+
const still = await system.describe(pid);
|
|
27820
|
+
if (still === void 0) return true;
|
|
27821
|
+
if (this.ours(still) === void 0) return false;
|
|
27822
|
+
log2(`Hermes's gateway, pid ${pid}, is still running ${this.timing.stopMs / 1e3} s after SIGTERM: SIGKILL`);
|
|
27823
|
+
system.signal(pid, "SIGKILL");
|
|
27824
|
+
return this.gone(pid, this.timing.killMs);
|
|
27825
|
+
}
|
|
27826
|
+
async gone(pid, withinMs) {
|
|
27827
|
+
const { system } = this.options;
|
|
27828
|
+
const by = system.now() + withinMs;
|
|
27829
|
+
while (system.alive(pid)) {
|
|
27830
|
+
if (system.now() >= by) return false;
|
|
27831
|
+
await system.sleep(this.timing.pollMs);
|
|
27832
|
+
}
|
|
27833
|
+
return true;
|
|
27834
|
+
}
|
|
27835
|
+
/** Hermes's own `hermes gateway restart|start`, then the gateway answering, from a new process. */
|
|
27836
|
+
async byService(gateway, verb, before) {
|
|
27837
|
+
const { system, env } = this.options;
|
|
27838
|
+
const hermes = system.which("hermes", env);
|
|
27839
|
+
if (hermes === void 0) return NO_HERMES;
|
|
27840
|
+
const scope = this.serviceFor(gateway)?.system === true ? ["--system"] : [];
|
|
27841
|
+
const ran = await system.run(hermes, ["gateway", verb, ...scope], gatewayEnv(env), this.timing.serviceMs);
|
|
27842
|
+
if (ran.code !== 0) return line(`hermes gateway ${verb} failed: ${whyFrom(ran.output, ran.code)}`);
|
|
27843
|
+
const by = system.now() + this.timing.serviceMs;
|
|
27844
|
+
for (; ; ) {
|
|
27845
|
+
const old = before !== void 0 && system.alive(before);
|
|
27846
|
+
if (!old && await system.healthy(gateway.url)) return void 0;
|
|
27847
|
+
if (system.now() >= by) {
|
|
27848
|
+
if (await system.healthy(gateway.url)) return void 0;
|
|
27849
|
+
return `Hermes's service ${verb === "start" ? "started" : "restarted"}, but nothing answers on port ${portOf(gateway.url)}.`;
|
|
27850
|
+
}
|
|
27851
|
+
await system.sleep(this.timing.pollMs);
|
|
27852
|
+
}
|
|
27853
|
+
}
|
|
27854
|
+
/**
|
|
27855
|
+
* `hermes <args>` (`gateway run`), on its own, in `cwd` (else the home), then through Homebrew's
|
|
27856
|
+
* expat when it died of macOS's.
|
|
27857
|
+
*/
|
|
27858
|
+
async start(gateway, args, cwd) {
|
|
27859
|
+
const { system, env, log: log2 } = this.options;
|
|
27860
|
+
const port = portOf(gateway.url);
|
|
27861
|
+
const hermes = system.which("hermes", env);
|
|
27862
|
+
if (hermes === void 0) return NO_HERMES;
|
|
27863
|
+
const folder2 = cwd ?? system.home;
|
|
27864
|
+
const plain = gatewayEnv(env);
|
|
27865
|
+
let outcome = await this.launch(gateway, hermes, args, plain, folder2, `hermes ${args.join(" ")}`);
|
|
27866
|
+
if (outcome.state === "ended" && outcome.early && outcome.output.includes(EXPAT_SYMBOL) && system.exists(EXPAT)) {
|
|
27867
|
+
const wrapper = wrapperOf(system.read(hermes) ?? "", plain);
|
|
27868
|
+
if (wrapper !== void 0) {
|
|
27869
|
+
const expat = dirname5(EXPAT);
|
|
27870
|
+
log2(
|
|
27871
|
+
`hermes gateway run stopped at once: Homebrew's Python can't load macOS's expat (${EXPAT_SYMBOL}). Starting ${wrapper.entry} directly with DYLD_LIBRARY_PATH=${expat}.`
|
|
27872
|
+
);
|
|
27873
|
+
const libraries = [expat, plain["DYLD_LIBRARY_PATH"]].filter((each) => each !== void 0 && each !== "").join(":");
|
|
27874
|
+
outcome = await this.launch(
|
|
27875
|
+
gateway,
|
|
27876
|
+
wrapper.entry,
|
|
27877
|
+
args,
|
|
27878
|
+
{ ...plain, ...wrapper.vars, DYLD_LIBRARY_PATH: libraries },
|
|
27879
|
+
folder2,
|
|
27880
|
+
`${wrapper.entry} ${args.join(" ")} with DYLD_LIBRARY_PATH=${expat}`
|
|
27881
|
+
);
|
|
27882
|
+
}
|
|
27883
|
+
}
|
|
27884
|
+
if (outcome.state === "up") {
|
|
27885
|
+
log2(`Hermes's gateway${outcome.pid === void 0 ? "" : `, pid ${outcome.pid},`} answers on port ${port}. It logs to ${this.options.logFile}.`);
|
|
27886
|
+
return void 0;
|
|
27887
|
+
}
|
|
27888
|
+
if (outcome.state === "silent") return `Hermes's gateway started, but nothing answers on port ${port}.`;
|
|
27889
|
+
return line(`Hermes's gateway stopped as it started: ${whyFrom(outcome.output, outcome.code)}`);
|
|
27890
|
+
}
|
|
27891
|
+
/** A program started, and watched until it answers, ends, or has had its time. */
|
|
27892
|
+
async launch(gateway, command, args, env, cwd, what) {
|
|
27893
|
+
const { system, logFile } = this.options;
|
|
27894
|
+
system.note(logFile, `--- ${new Date(system.now()).toISOString()} chips-hermes started ${what}`);
|
|
27895
|
+
const from = system.size(logFile);
|
|
27896
|
+
const startedAt = system.now();
|
|
27897
|
+
const child = system.launch({ command, args, env, cwd, log: logFile });
|
|
27898
|
+
let ended;
|
|
27899
|
+
void child.exited.then(({ code }) => {
|
|
27900
|
+
ended = { code, at: system.now() };
|
|
27901
|
+
});
|
|
27902
|
+
for (; ; ) {
|
|
27903
|
+
await Promise.resolve();
|
|
27904
|
+
if (ended !== void 0) {
|
|
27905
|
+
return { state: "ended", early: ended.at - startedAt <= this.timing.earlyMs, output: system.since(logFile, from), code: ended.code };
|
|
27906
|
+
}
|
|
27907
|
+
if (await system.healthy(gateway.url)) return { state: "up", pid: child.pid };
|
|
27908
|
+
if (system.now() - startedAt >= this.timing.upMs) return { state: "silent" };
|
|
27909
|
+
await system.sleep(this.timing.pollMs);
|
|
27910
|
+
}
|
|
27911
|
+
}
|
|
27912
|
+
};
|
|
27913
|
+
function restarter(deps) {
|
|
27914
|
+
let running;
|
|
27915
|
+
return (ask2) => {
|
|
27916
|
+
if (running !== void 0) {
|
|
27917
|
+
deps.log(`restart asked by ${ask2.askedBy} while one runs: it is the same restart`);
|
|
27918
|
+
return running;
|
|
27919
|
+
}
|
|
27920
|
+
running = (async () => {
|
|
27921
|
+
deps.log(`restart asked by ${ask2.askedBy}: restarting Hermes's gateway`);
|
|
27922
|
+
deps.hold(true);
|
|
27923
|
+
let trouble;
|
|
27924
|
+
try {
|
|
27925
|
+
trouble = await deps.restart();
|
|
27926
|
+
} catch (error62) {
|
|
27927
|
+
trouble = line(`Restart failed: ${error62 instanceof Error ? error62.message : String(error62)}`);
|
|
27928
|
+
}
|
|
27929
|
+
deps.log(trouble === void 0 ? "restarted, and Hermes answers" : `restart: ${trouble}`);
|
|
27930
|
+
try {
|
|
27931
|
+
await deps.say(trouble);
|
|
27932
|
+
} catch (error62) {
|
|
27933
|
+
deps.log(`could not tell Chips how the restart went: ${String(error62)}`);
|
|
27934
|
+
} finally {
|
|
27935
|
+
deps.hold(false);
|
|
27936
|
+
}
|
|
27937
|
+
})().finally(() => {
|
|
27938
|
+
running = void 0;
|
|
27939
|
+
});
|
|
27940
|
+
return running;
|
|
27941
|
+
};
|
|
27942
|
+
}
|
|
27943
|
+
function gatewayLog(configFolder, platform = process.platform) {
|
|
27944
|
+
return platform === "darwin" ? join9(homedir5(), "Library", "Logs", "chips-hermes-gateway.log") : join9(configFolder, "gateway.log");
|
|
27945
|
+
}
|
|
27946
|
+
function runToEnd(command, args, options) {
|
|
27947
|
+
return new Promise((done) => {
|
|
27948
|
+
const child = spawn2(command, args, { env: options.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
27949
|
+
let output2 = "";
|
|
27950
|
+
const keep = (data) => {
|
|
27951
|
+
output2 = (output2 + String(data)).slice(-8e3);
|
|
27952
|
+
};
|
|
27953
|
+
child.stdout.on("data", keep);
|
|
27954
|
+
child.stderr.on("data", keep);
|
|
27955
|
+
const limit = setTimeout(() => child.kill("SIGTERM"), options.timeoutMs);
|
|
27956
|
+
child.on("error", (error62) => {
|
|
27957
|
+
clearTimeout(limit);
|
|
27958
|
+
done({ code: null, output: error62.message });
|
|
27959
|
+
});
|
|
27960
|
+
child.on("close", (code) => {
|
|
27961
|
+
clearTimeout(limit);
|
|
27962
|
+
done({ code, output: output2 });
|
|
27963
|
+
});
|
|
27964
|
+
});
|
|
27965
|
+
}
|
|
27966
|
+
function thisMachine() {
|
|
27967
|
+
return {
|
|
27968
|
+
platform: process.platform,
|
|
27969
|
+
uid: process.getuid?.() ?? -1,
|
|
27970
|
+
home: homedir5(),
|
|
27971
|
+
exists: existsSync8,
|
|
27972
|
+
read(path) {
|
|
27973
|
+
try {
|
|
27974
|
+
return readFileSync8(path, "utf8");
|
|
27975
|
+
} catch {
|
|
27976
|
+
return void 0;
|
|
27977
|
+
}
|
|
27978
|
+
},
|
|
27979
|
+
which(command, env) {
|
|
27980
|
+
for (const folder2 of (env["PATH"] ?? "").split(delimiter2)) {
|
|
27981
|
+
if (folder2 === "") continue;
|
|
27982
|
+
const path = join9(folder2, command);
|
|
27983
|
+
try {
|
|
27984
|
+
accessSync2(path, constants2.X_OK);
|
|
27985
|
+
return path;
|
|
27986
|
+
} catch {
|
|
27987
|
+
}
|
|
27988
|
+
}
|
|
27989
|
+
return void 0;
|
|
27990
|
+
},
|
|
27991
|
+
async listener(port) {
|
|
27992
|
+
const { output: output2 } = await runToEnd("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-Fp"], { timeoutMs: 5e3 });
|
|
27993
|
+
const pid = Number(/^p(\d+)$/m.exec(output2)?.[1]);
|
|
27994
|
+
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
27995
|
+
},
|
|
27996
|
+
async describe(pid) {
|
|
27997
|
+
const { code, output: output2 } = await runToEnd("ps", ["-ww", "-o", "uid=", "-o", "command=", "-p", String(pid)], { timeoutMs: 5e3 });
|
|
27998
|
+
const found = /^\s*(\d+)\s+(.*)$/m.exec(output2);
|
|
27999
|
+
if (code !== 0 || found === null) return void 0;
|
|
28000
|
+
return { uid: Number(found[1]), command: found[2] ?? "", cwd: processFolder(pid) };
|
|
28001
|
+
},
|
|
28002
|
+
alive(pid) {
|
|
28003
|
+
try {
|
|
28004
|
+
process.kill(pid, 0);
|
|
28005
|
+
return true;
|
|
28006
|
+
} catch (error62) {
|
|
28007
|
+
return error62.code === "EPERM";
|
|
28008
|
+
}
|
|
28009
|
+
},
|
|
28010
|
+
signal(pid, signal) {
|
|
28011
|
+
try {
|
|
28012
|
+
process.kill(pid, signal);
|
|
28013
|
+
} catch {
|
|
28014
|
+
}
|
|
28015
|
+
},
|
|
28016
|
+
run: (command, args, env, timeoutMs) => runToEnd(command, args, { env, timeoutMs }),
|
|
28017
|
+
launch({ command, args, env, cwd, log: log2 }) {
|
|
28018
|
+
const out = openSync2(log2, "a");
|
|
28019
|
+
try {
|
|
28020
|
+
const child = spawn2(command, args, { cwd, env, detached: true, stdio: ["ignore", out, out] });
|
|
28021
|
+
const exited = new Promise((done) => {
|
|
28022
|
+
child.once("exit", (code, signal) => done({ code, signal }));
|
|
28023
|
+
child.once("error", (error62) => {
|
|
28024
|
+
appendFileSync2(log2, `${error62.message}
|
|
28025
|
+
`);
|
|
28026
|
+
done({ code: null, signal: null });
|
|
28027
|
+
});
|
|
28028
|
+
});
|
|
28029
|
+
child.unref();
|
|
28030
|
+
return { pid: child.pid, exited };
|
|
28031
|
+
} finally {
|
|
28032
|
+
closeSync2(out);
|
|
28033
|
+
}
|
|
28034
|
+
},
|
|
28035
|
+
note(log2, words4) {
|
|
28036
|
+
appendFileSync2(log2, `${words4}
|
|
28037
|
+
`);
|
|
28038
|
+
},
|
|
28039
|
+
size(log2) {
|
|
28040
|
+
try {
|
|
28041
|
+
return statSync4(log2).size;
|
|
28042
|
+
} catch {
|
|
28043
|
+
return 0;
|
|
28044
|
+
}
|
|
28045
|
+
},
|
|
28046
|
+
since(log2, from) {
|
|
28047
|
+
try {
|
|
28048
|
+
const to = statSync4(log2).size;
|
|
28049
|
+
const start = Math.max(from, to - 64e3);
|
|
28050
|
+
const buffer = Buffer.alloc(Math.max(0, to - start));
|
|
28051
|
+
const fd = openSync2(log2, "r");
|
|
28052
|
+
try {
|
|
28053
|
+
readSync(fd, buffer, 0, buffer.length, start);
|
|
28054
|
+
} finally {
|
|
28055
|
+
closeSync2(fd);
|
|
28056
|
+
}
|
|
28057
|
+
return buffer.toString("utf8");
|
|
28058
|
+
} catch {
|
|
28059
|
+
return "";
|
|
28060
|
+
}
|
|
28061
|
+
},
|
|
28062
|
+
healthy: (url2) => new HermesClient({ url: url2, key: "" }, (input2, init) => fetch(input2, { ...init, signal: AbortSignal.timeout(5e3) })).healthy(),
|
|
28063
|
+
sleep: (ms) => new Promise((done) => setTimeout(done, ms)),
|
|
28064
|
+
now: () => Date.now()
|
|
28065
|
+
};
|
|
28066
|
+
}
|
|
28067
|
+
|
|
28068
|
+
// src/keep.ts
|
|
28069
|
+
import { homedir as homedir6 } from "node:os";
|
|
28070
|
+
import { basename as basename5, join as join10, resolve as resolve4 } from "node:path";
|
|
27586
28071
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
27587
28072
|
var flags = process.argv.slice(2);
|
|
27588
28073
|
var words3 = flags.filter((word) => !word.startsWith("--"));
|
|
27589
|
-
var whole = (word) =>
|
|
28074
|
+
var whole = (word) => resolve4(word.startsWith("~/") ? join10(homedir6(), word.slice(2)) : word);
|
|
27590
28075
|
var here = fileURLToPath2(new URL("..", import.meta.url)).replace(/\/$/, "");
|
|
27591
28076
|
var self = fileURLToPath2(import.meta.url);
|
|
27592
28077
|
var run = self.endsWith(".js") ? [self] : ["--import", "tsx", "src/main.ts"];
|
|
27593
|
-
var again = process.env["npm_lifecycle_event"] === "keep" ? "npm run keep --" :
|
|
28078
|
+
var again = process.env["npm_lifecycle_event"] === "keep" ? "npm run keep --" : basename5(process.argv[1] ?? "chips");
|
|
27594
28079
|
var stopLine = process.env["CHIPS_STOP_COMMAND"] || [again, ...words3, "--stop"].join(" ");
|
|
27595
|
-
var end = (
|
|
27596
|
-
(code === 0 ? process.stdout : process.stderr).write(`${
|
|
28080
|
+
var end = (line2, code) => {
|
|
28081
|
+
(code === 0 ? process.stdout : process.stderr).write(`${line2}
|
|
27597
28082
|
`);
|
|
27598
28083
|
process.exit(code);
|
|
27599
28084
|
};
|
|
@@ -27632,9 +28117,9 @@ function keepIfAsked(label2, url2, names, log2, before = () => void 0) {
|
|
|
27632
28117
|
}
|
|
27633
28118
|
|
|
27634
28119
|
// src/setup.ts
|
|
27635
|
-
import { existsSync as
|
|
27636
|
-
import { homedir as
|
|
27637
|
-
import { join as
|
|
28120
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9 } from "node:fs";
|
|
28121
|
+
import { homedir as homedir7 } from "node:os";
|
|
28122
|
+
import { join as join11, resolve as resolve5 } from "node:path";
|
|
27638
28123
|
var SetupError = class extends Error {
|
|
27639
28124
|
constructor(message, words4 = {}) {
|
|
27640
28125
|
super(message);
|
|
@@ -27643,21 +28128,21 @@ var SetupError = class extends Error {
|
|
|
27643
28128
|
words;
|
|
27644
28129
|
name = "SetupError";
|
|
27645
28130
|
};
|
|
27646
|
-
var shown = (path) => path.startsWith(`${
|
|
27647
|
-
var
|
|
28131
|
+
var shown = (path) => path.startsWith(`${homedir7()}/`) ? `~${path.slice(homedir7().length)}` : path;
|
|
28132
|
+
var portOf2 = (url2) => {
|
|
27648
28133
|
const parsed = new URL(url2);
|
|
27649
28134
|
return parsed.port || (parsed.protocol === "https:" ? "443" : "80");
|
|
27650
28135
|
};
|
|
27651
|
-
function checkedWords(config3,
|
|
28136
|
+
function checkedWords(config3, restart2 = false) {
|
|
27652
28137
|
const urls = new Set(config3.agents.map((agent) => agent.address.url));
|
|
27653
28138
|
const [url2] = urls;
|
|
27654
|
-
const found = urls.size === 1 && url2 !== void 0 ? `Its API server answers on port ${
|
|
27655
|
-
return
|
|
28139
|
+
const found = urls.size === 1 && url2 !== void 0 ? `Its API server answers on port ${portOf2(url2)}` : `Its API server answers for each of ${config3.agents.length} agents`;
|
|
28140
|
+
return restart2 ? `${found}. ${RESTART}` : found;
|
|
27656
28141
|
}
|
|
27657
28142
|
var RESTART = "Start hermes gateway again once, so it tells Chips how full each agent's context is.";
|
|
27658
28143
|
function readyHooks(config3, options) {
|
|
27659
28144
|
const lines2 = [];
|
|
27660
|
-
let
|
|
28145
|
+
let restart2 = false;
|
|
27661
28146
|
const done = /* @__PURE__ */ new Set();
|
|
27662
28147
|
for (const agent of config3.agents) {
|
|
27663
28148
|
const profile = profileName(agent.address.profile);
|
|
@@ -27668,12 +28153,12 @@ function readyHooks(config3, options) {
|
|
|
27668
28153
|
try {
|
|
27669
28154
|
outcome = addHook(home2, options.port, profile, options.secret);
|
|
27670
28155
|
} catch (error62) {
|
|
27671
|
-
lines2.push(`Chips's webhook wasn't added to ${shown(
|
|
28156
|
+
lines2.push(`Chips's webhook wasn't added to ${shown(join11(home2, "config.yaml"))}: ${error62 instanceof Error ? error62.message : String(error62)}`);
|
|
27672
28157
|
continue;
|
|
27673
28158
|
}
|
|
27674
28159
|
if (outcome.state === "added") {
|
|
27675
28160
|
lines2.push(`Added Chips's webhook to ${shown(outcome.path)}, so Hermes tells Chips how full each agent's context is.`);
|
|
27676
|
-
|
|
28161
|
+
restart2 ||= options.gatewayUp;
|
|
27677
28162
|
} else if (outcome.state === "by_hand") {
|
|
27678
28163
|
lines2.push(
|
|
27679
28164
|
`${shown(outcome.path)} was left as it is: ${outcome.why}. For Chips to show how full each agent's context is, add these lines to it, then start hermes gateway again:
|
|
@@ -27683,30 +28168,30 @@ ${outcome.lines}`
|
|
|
27683
28168
|
lines2.push(`Hermes has no ${profile} profile at ${shown(home2)}, so no webhook was added for it.`);
|
|
27684
28169
|
}
|
|
27685
28170
|
}
|
|
27686
|
-
if (
|
|
27687
|
-
return { lines: lines2, restart };
|
|
28171
|
+
if (restart2) lines2.push(RESTART);
|
|
28172
|
+
return { lines: lines2, restart: restart2 };
|
|
27688
28173
|
}
|
|
27689
28174
|
var STILL_GOOD = "The link is still good: run the same command again once that's done.";
|
|
27690
28175
|
var EXAMPLE = '{ "agents": [{ "name": "Hermes", "profile": "default", "keyEnv": "API_SERVER_KEY" }] }';
|
|
27691
28176
|
var PLAIN = { agents: [{ name: "Hermes", profile: "default", keyEnv: "API_SERVER_KEY" }] };
|
|
27692
28177
|
function hermesEnvPath(env = process.env) {
|
|
27693
|
-
return
|
|
28178
|
+
return resolve5(env["HERMES_HOME"] ?? resolve5(homedir7(), ".hermes"), ".env");
|
|
27694
28179
|
}
|
|
27695
28180
|
function fromHermes(name2, env) {
|
|
27696
28181
|
const path = hermesEnvPath(env);
|
|
27697
|
-
if (!
|
|
27698
|
-
for (const
|
|
27699
|
-
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/.exec(
|
|
28182
|
+
if (!existsSync9(path)) return void 0;
|
|
28183
|
+
for (const line2 of readFileSync9(path, "utf8").split("\n")) {
|
|
28184
|
+
const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/.exec(line2);
|
|
27700
28185
|
if (match?.[1] === name2) return match[2]?.replace(/^(["'])(.*)\1$/, "$2");
|
|
27701
28186
|
}
|
|
27702
28187
|
return void 0;
|
|
27703
28188
|
}
|
|
27704
28189
|
function folderOf(said2) {
|
|
27705
28190
|
if (typeof said2 !== "string" || said2 === "") return void 0;
|
|
27706
|
-
return
|
|
28191
|
+
return resolve5(said2 === "~" || said2.startsWith("~/") ? homedir7() + said2.slice(1) : said2);
|
|
27707
28192
|
}
|
|
27708
28193
|
function readConfig(path, env = process.env) {
|
|
27709
|
-
const raw =
|
|
28194
|
+
const raw = existsSync9(path) ? JSON.parse(readFileSync9(path, "utf8")) : PLAIN;
|
|
27710
28195
|
const url2 = typeof raw.url === "string" ? raw.url : "http://127.0.0.1:8642";
|
|
27711
28196
|
const folder2 = folderOf(raw.folder);
|
|
27712
28197
|
if (!Array.isArray(raw.agents) || raw.agents.length === 0) {
|
|
@@ -27745,7 +28230,7 @@ function readConfig(path, env = process.env) {
|
|
|
27745
28230
|
return {
|
|
27746
28231
|
...typeof raw.name === "string" ? { name: raw.name } : {},
|
|
27747
28232
|
agents,
|
|
27748
|
-
folders: folderOf(raw.folders) ??
|
|
28233
|
+
folders: folderOf(raw.folders) ?? resolve5(homedir7(), "chips-hermes", "agents"),
|
|
27749
28234
|
...typeof hookPort === "number" && Number.isInteger(hookPort) && hookPort > 0 && hookPort < 65536 ? { hookPort } : {}
|
|
27750
28235
|
};
|
|
27751
28236
|
}
|
|
@@ -27756,7 +28241,7 @@ async function checkHermes(config3, clientFor = (agent) => new HermesClient(agen
|
|
|
27756
28241
|
throw new SetupError(
|
|
27757
28242
|
`Hermes isn't answering for ${agent.name} at ${agent.address.url}: start its API server (API_SERVER_ENABLED=true, then hermes gateway). ${STILL_GOOD}`,
|
|
27758
28243
|
{
|
|
27759
|
-
detail: `Nothing answers on port ${
|
|
28244
|
+
detail: `Nothing answers on port ${portOf2(agent.address.url)}`,
|
|
27760
28245
|
says: "Hermes's API server is off.",
|
|
27761
28246
|
doing: `Add this line to ${shown(hermesEnvPath())}, then start Hermes with hermes gateway.`,
|
|
27762
28247
|
copy: "API_SERVER_ENABLED=true"
|
|
@@ -27777,20 +28262,20 @@ async function checkHermes(config3, clientFor = (agent) => new HermesClient(agen
|
|
|
27777
28262
|
}
|
|
27778
28263
|
|
|
27779
28264
|
// src/main.ts
|
|
27780
|
-
var home = (path) => path.startsWith("~/") ?
|
|
28265
|
+
var home = (path) => path.startsWith("~/") ? join12(homedir8(), path.slice(2)) : path;
|
|
27781
28266
|
var link = process.env["CHIPS_JOIN_LINK"] || void 0;
|
|
27782
28267
|
var LABEL = "chat.chips.hermes";
|
|
27783
28268
|
stopIfAsked(LABEL);
|
|
27784
|
-
var configPath =
|
|
27785
|
-
var folder =
|
|
27786
|
-
var statePath =
|
|
27787
|
-
var state =
|
|
28269
|
+
var configPath = resolve6(home(words3[0] ?? "~/.chips-hermes/config.json"));
|
|
28270
|
+
var folder = dirname6(configPath);
|
|
28271
|
+
var statePath = join12(folder, "sessions.json");
|
|
28272
|
+
var state = existsSync10(statePath) ? JSON.parse(readFileSync10(statePath, "utf8")) : { sessions: {} };
|
|
27788
28273
|
var saveState = (next) => {
|
|
27789
28274
|
mkdirSync6(folder, { recursive: true, mode: 448 });
|
|
27790
28275
|
writeFileSync5(statePath, `${JSON.stringify(next, null, 2)}
|
|
27791
28276
|
`, { mode: 384 });
|
|
27792
28277
|
};
|
|
27793
|
-
var log = (
|
|
28278
|
+
var log = (line2) => process.stdout.write(`${(/* @__PURE__ */ new Date()).toISOString()} ${line2}
|
|
27794
28279
|
`);
|
|
27795
28280
|
var freePort = () => new Promise((done, failed) => {
|
|
27796
28281
|
const server = createServer2();
|
|
@@ -27815,7 +28300,7 @@ try {
|
|
|
27815
28300
|
const first = config2.agents[0];
|
|
27816
28301
|
const gatewayUp = link !== void 0 || first !== void 0 && await new HermesClient(first.address).healthy();
|
|
27817
28302
|
const hooks = readyHooks(config2, { root, port: fills.port, secret: fills.secret, gatewayUp });
|
|
27818
|
-
for (const
|
|
28303
|
+
for (const line2 of hooks.lines) log(line2);
|
|
27819
28304
|
if (link !== void 0) reportStep("checked", { detail: checkedWords(config2, hooks.restart) });
|
|
27820
28305
|
} catch (error62) {
|
|
27821
28306
|
if (!(error62 instanceof SetupError)) throw error62;
|
|
@@ -27825,6 +28310,15 @@ try {
|
|
|
27825
28310
|
process.exit(1);
|
|
27826
28311
|
}
|
|
27827
28312
|
var name = config2.name ?? "Hermes";
|
|
28313
|
+
var gateways2 = new Gateways({
|
|
28314
|
+
gateways: gatewaysOf(config2.agents),
|
|
28315
|
+
root,
|
|
28316
|
+
env: process.env,
|
|
28317
|
+
system: thisMachine(),
|
|
28318
|
+
log: (line2) => log(`gateway: ${line2}`),
|
|
28319
|
+
logFile: gatewayLog(folder)
|
|
28320
|
+
});
|
|
28321
|
+
if (link === void 0) await gateways2.startIfDown();
|
|
27828
28322
|
var refused2 = (error62) => {
|
|
27829
28323
|
if (!(error62 instanceof JoinError)) throw error62;
|
|
27830
28324
|
process.stderr.write(`${error62.message}
|
|
@@ -27841,11 +28335,21 @@ var joined = await join3({
|
|
|
27841
28335
|
}).catch(refused2);
|
|
27842
28336
|
keepIfAsked(LABEL, joined.url, ["HERMES_HOME", ...config2.agents.map((agent) => agent.keyEnv ?? "API_SERVER_KEY")], log);
|
|
27843
28337
|
var bridge;
|
|
28338
|
+
var restart = restarter({
|
|
28339
|
+
restart: () => gateways2.restart(),
|
|
28340
|
+
hold: (held) => bridge?.hold(held),
|
|
28341
|
+
say: async (trouble) => {
|
|
28342
|
+
if (bridge !== void 0) return bridge.restarted(trouble);
|
|
28343
|
+
await (trouble === void 0 ? chips.reportOk() : chips.reportTrouble(trouble));
|
|
28344
|
+
},
|
|
28345
|
+
log: (line2) => log(`gateway: ${line2}`)
|
|
28346
|
+
});
|
|
27844
28347
|
var chips = await connect({
|
|
27845
28348
|
url: joined.url,
|
|
27846
28349
|
token: joined.token,
|
|
27847
28350
|
client: { name: "chips-hermes", version: "1.0.0" },
|
|
27848
|
-
store: fileStore(
|
|
28351
|
+
store: fileStore(join12(folder, "chips-state.json")),
|
|
28352
|
+
onRestart: restart,
|
|
27849
28353
|
settings: settingsFor(() => bridge)
|
|
27850
28354
|
});
|
|
27851
28355
|
var git = await checkGit();
|