@perkos/perkos-a2a 0.8.35 → 0.9.0
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 +99 -21
- package/dist/agent.d.ts +14 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +363 -0
- package/dist/agent.js.map +1 -0
- package/dist/hermes-cli.d.ts +30 -0
- package/dist/hermes-cli.d.ts.map +1 -0
- package/dist/hermes-cli.js +120 -0
- package/dist/hermes-cli.js.map +1 -0
- package/dist/hermes-plugin.d.ts +91 -0
- package/dist/hermes-plugin.d.ts.map +1 -0
- package/dist/hermes-plugin.js +196 -0
- package/dist/hermes-plugin.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1022 -26
- package/dist/index.js.map +4 -4
- package/dist/types.d.ts +36 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -1
- package/dist/types.js.map +1 -1
- package/docs/chat-client.md +190 -0
- package/docs/demo-setup.md +273 -0
- package/openclaw.plugin.json +38 -1
- package/package.json +33 -1
- package/scripts/hermes/install.mjs +211 -0
package/dist/index.js
CHANGED
|
@@ -18408,7 +18408,7 @@ var require_view = __commonJS({
|
|
|
18408
18408
|
var dirname = path.dirname;
|
|
18409
18409
|
var basename = path.basename;
|
|
18410
18410
|
var extname = path.extname;
|
|
18411
|
-
var
|
|
18411
|
+
var join2 = path.join;
|
|
18412
18412
|
var resolve = path.resolve;
|
|
18413
18413
|
module.exports = View;
|
|
18414
18414
|
function View(name, options) {
|
|
@@ -18456,12 +18456,12 @@ var require_view = __commonJS({
|
|
|
18456
18456
|
};
|
|
18457
18457
|
View.prototype.resolve = function resolve2(dir, file) {
|
|
18458
18458
|
var ext = this.ext;
|
|
18459
|
-
var path2 =
|
|
18459
|
+
var path2 = join2(dir, file);
|
|
18460
18460
|
var stat = tryStat(path2);
|
|
18461
18461
|
if (stat && stat.isFile()) {
|
|
18462
18462
|
return path2;
|
|
18463
18463
|
}
|
|
18464
|
-
path2 =
|
|
18464
|
+
path2 = join2(dir, basename(file, ext), "index" + ext);
|
|
18465
18465
|
stat = tryStat(path2);
|
|
18466
18466
|
if (stat && stat.isFile()) {
|
|
18467
18467
|
return path2;
|
|
@@ -19094,7 +19094,7 @@ var require_send = __commonJS({
|
|
|
19094
19094
|
var Stream = __require("stream");
|
|
19095
19095
|
var util = __require("util");
|
|
19096
19096
|
var extname = path.extname;
|
|
19097
|
-
var
|
|
19097
|
+
var join2 = path.join;
|
|
19098
19098
|
var normalize = path.normalize;
|
|
19099
19099
|
var resolve = path.resolve;
|
|
19100
19100
|
var sep = path.sep;
|
|
@@ -19313,7 +19313,7 @@ var require_send = __commonJS({
|
|
|
19313
19313
|
return res;
|
|
19314
19314
|
}
|
|
19315
19315
|
parts = path2.split(sep);
|
|
19316
|
-
path2 = normalize(
|
|
19316
|
+
path2 = normalize(join2(root, path2));
|
|
19317
19317
|
} else {
|
|
19318
19318
|
if (UP_PATH_REGEXP.test(path2)) {
|
|
19319
19319
|
debug('malicious path "%s"', path2);
|
|
@@ -19448,7 +19448,7 @@ var require_send = __commonJS({
|
|
|
19448
19448
|
if (err) return self.onStatError(err);
|
|
19449
19449
|
return self.error(404);
|
|
19450
19450
|
}
|
|
19451
|
-
var p =
|
|
19451
|
+
var p = join2(path2, self._index[i]);
|
|
19452
19452
|
debug('stat "%s"', p);
|
|
19453
19453
|
fs.stat(p, function(err2, stat) {
|
|
19454
19454
|
if (err2) return next(err2);
|
|
@@ -22608,6 +22608,218 @@ var require_constants = __commonJS({
|
|
|
22608
22608
|
}
|
|
22609
22609
|
});
|
|
22610
22610
|
|
|
22611
|
+
// ../../../../node_modules/node-gyp-build/node-gyp-build.js
|
|
22612
|
+
var require_node_gyp_build = __commonJS({
|
|
22613
|
+
"../../../../node_modules/node-gyp-build/node-gyp-build.js"(exports, module) {
|
|
22614
|
+
var fs = __require("fs");
|
|
22615
|
+
var path = __require("path");
|
|
22616
|
+
var os = __require("os");
|
|
22617
|
+
var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
22618
|
+
var vars = process.config && process.config.variables || {};
|
|
22619
|
+
var prebuildsOnly = !!process.env.PREBUILDS_ONLY;
|
|
22620
|
+
var abi = process.versions.modules;
|
|
22621
|
+
var runtime = isElectron() ? "electron" : isNwjs() ? "node-webkit" : "node";
|
|
22622
|
+
var arch = process.env.npm_config_arch || os.arch();
|
|
22623
|
+
var platform = process.env.npm_config_platform || os.platform();
|
|
22624
|
+
var libc = process.env.LIBC || (isAlpine(platform) ? "musl" : "glibc");
|
|
22625
|
+
var armv = process.env.ARM_VERSION || (arch === "arm64" ? "8" : vars.arm_version) || "";
|
|
22626
|
+
var uv = (process.versions.uv || "").split(".")[0];
|
|
22627
|
+
module.exports = load;
|
|
22628
|
+
function load(dir) {
|
|
22629
|
+
return runtimeRequire(load.resolve(dir));
|
|
22630
|
+
}
|
|
22631
|
+
load.resolve = load.path = function(dir) {
|
|
22632
|
+
dir = path.resolve(dir || ".");
|
|
22633
|
+
try {
|
|
22634
|
+
var name = runtimeRequire(path.join(dir, "package.json")).name.toUpperCase().replace(/-/g, "_");
|
|
22635
|
+
if (process.env[name + "_PREBUILD"]) dir = process.env[name + "_PREBUILD"];
|
|
22636
|
+
} catch (err) {
|
|
22637
|
+
}
|
|
22638
|
+
if (!prebuildsOnly) {
|
|
22639
|
+
var release = getFirst(path.join(dir, "build/Release"), matchBuild);
|
|
22640
|
+
if (release) return release;
|
|
22641
|
+
var debug = getFirst(path.join(dir, "build/Debug"), matchBuild);
|
|
22642
|
+
if (debug) return debug;
|
|
22643
|
+
}
|
|
22644
|
+
var prebuild = resolve(dir);
|
|
22645
|
+
if (prebuild) return prebuild;
|
|
22646
|
+
var nearby = resolve(path.dirname(process.execPath));
|
|
22647
|
+
if (nearby) return nearby;
|
|
22648
|
+
var target = [
|
|
22649
|
+
"platform=" + platform,
|
|
22650
|
+
"arch=" + arch,
|
|
22651
|
+
"runtime=" + runtime,
|
|
22652
|
+
"abi=" + abi,
|
|
22653
|
+
"uv=" + uv,
|
|
22654
|
+
armv ? "armv=" + armv : "",
|
|
22655
|
+
"libc=" + libc,
|
|
22656
|
+
"node=" + process.versions.node,
|
|
22657
|
+
process.versions.electron ? "electron=" + process.versions.electron : "",
|
|
22658
|
+
typeof __webpack_require__ === "function" ? "webpack=true" : ""
|
|
22659
|
+
// eslint-disable-line
|
|
22660
|
+
].filter(Boolean).join(" ");
|
|
22661
|
+
throw new Error("No native build was found for " + target + "\n loaded from: " + dir + "\n");
|
|
22662
|
+
function resolve(dir2) {
|
|
22663
|
+
var tuples = readdirSync(path.join(dir2, "prebuilds")).map(parseTuple);
|
|
22664
|
+
var tuple = tuples.filter(matchTuple(platform, arch)).sort(compareTuples)[0];
|
|
22665
|
+
if (!tuple) return;
|
|
22666
|
+
var prebuilds = path.join(dir2, "prebuilds", tuple.name);
|
|
22667
|
+
var parsed = readdirSync(prebuilds).map(parseTags);
|
|
22668
|
+
var candidates = parsed.filter(matchTags(runtime, abi));
|
|
22669
|
+
var winner = candidates.sort(compareTags(runtime))[0];
|
|
22670
|
+
if (winner) return path.join(prebuilds, winner.file);
|
|
22671
|
+
}
|
|
22672
|
+
};
|
|
22673
|
+
function readdirSync(dir) {
|
|
22674
|
+
try {
|
|
22675
|
+
return fs.readdirSync(dir);
|
|
22676
|
+
} catch (err) {
|
|
22677
|
+
return [];
|
|
22678
|
+
}
|
|
22679
|
+
}
|
|
22680
|
+
function getFirst(dir, filter) {
|
|
22681
|
+
var files = readdirSync(dir).filter(filter);
|
|
22682
|
+
return files[0] && path.join(dir, files[0]);
|
|
22683
|
+
}
|
|
22684
|
+
function matchBuild(name) {
|
|
22685
|
+
return /\.node$/.test(name);
|
|
22686
|
+
}
|
|
22687
|
+
function parseTuple(name) {
|
|
22688
|
+
var arr = name.split("-");
|
|
22689
|
+
if (arr.length !== 2) return;
|
|
22690
|
+
var platform2 = arr[0];
|
|
22691
|
+
var architectures = arr[1].split("+");
|
|
22692
|
+
if (!platform2) return;
|
|
22693
|
+
if (!architectures.length) return;
|
|
22694
|
+
if (!architectures.every(Boolean)) return;
|
|
22695
|
+
return { name, platform: platform2, architectures };
|
|
22696
|
+
}
|
|
22697
|
+
function matchTuple(platform2, arch2) {
|
|
22698
|
+
return function(tuple) {
|
|
22699
|
+
if (tuple == null) return false;
|
|
22700
|
+
if (tuple.platform !== platform2) return false;
|
|
22701
|
+
return tuple.architectures.includes(arch2);
|
|
22702
|
+
};
|
|
22703
|
+
}
|
|
22704
|
+
function compareTuples(a, b) {
|
|
22705
|
+
return a.architectures.length - b.architectures.length;
|
|
22706
|
+
}
|
|
22707
|
+
function parseTags(file) {
|
|
22708
|
+
var arr = file.split(".");
|
|
22709
|
+
var extension2 = arr.pop();
|
|
22710
|
+
var tags = { file, specificity: 0 };
|
|
22711
|
+
if (extension2 !== "node") return;
|
|
22712
|
+
for (var i = 0; i < arr.length; i++) {
|
|
22713
|
+
var tag = arr[i];
|
|
22714
|
+
if (tag === "node" || tag === "electron" || tag === "node-webkit") {
|
|
22715
|
+
tags.runtime = tag;
|
|
22716
|
+
} else if (tag === "napi") {
|
|
22717
|
+
tags.napi = true;
|
|
22718
|
+
} else if (tag.slice(0, 3) === "abi") {
|
|
22719
|
+
tags.abi = tag.slice(3);
|
|
22720
|
+
} else if (tag.slice(0, 2) === "uv") {
|
|
22721
|
+
tags.uv = tag.slice(2);
|
|
22722
|
+
} else if (tag.slice(0, 4) === "armv") {
|
|
22723
|
+
tags.armv = tag.slice(4);
|
|
22724
|
+
} else if (tag === "glibc" || tag === "musl") {
|
|
22725
|
+
tags.libc = tag;
|
|
22726
|
+
} else {
|
|
22727
|
+
continue;
|
|
22728
|
+
}
|
|
22729
|
+
tags.specificity++;
|
|
22730
|
+
}
|
|
22731
|
+
return tags;
|
|
22732
|
+
}
|
|
22733
|
+
function matchTags(runtime2, abi2) {
|
|
22734
|
+
return function(tags) {
|
|
22735
|
+
if (tags == null) return false;
|
|
22736
|
+
if (tags.runtime && tags.runtime !== runtime2 && !runtimeAgnostic(tags)) return false;
|
|
22737
|
+
if (tags.abi && tags.abi !== abi2 && !tags.napi) return false;
|
|
22738
|
+
if (tags.uv && tags.uv !== uv) return false;
|
|
22739
|
+
if (tags.armv && tags.armv !== armv) return false;
|
|
22740
|
+
if (tags.libc && tags.libc !== libc) return false;
|
|
22741
|
+
return true;
|
|
22742
|
+
};
|
|
22743
|
+
}
|
|
22744
|
+
function runtimeAgnostic(tags) {
|
|
22745
|
+
return tags.runtime === "node" && tags.napi;
|
|
22746
|
+
}
|
|
22747
|
+
function compareTags(runtime2) {
|
|
22748
|
+
return function(a, b) {
|
|
22749
|
+
if (a.runtime !== b.runtime) {
|
|
22750
|
+
return a.runtime === runtime2 ? -1 : 1;
|
|
22751
|
+
} else if (a.abi !== b.abi) {
|
|
22752
|
+
return a.abi ? -1 : 1;
|
|
22753
|
+
} else if (a.specificity !== b.specificity) {
|
|
22754
|
+
return a.specificity > b.specificity ? -1 : 1;
|
|
22755
|
+
} else {
|
|
22756
|
+
return 0;
|
|
22757
|
+
}
|
|
22758
|
+
};
|
|
22759
|
+
}
|
|
22760
|
+
function isNwjs() {
|
|
22761
|
+
return !!(process.versions && process.versions.nw);
|
|
22762
|
+
}
|
|
22763
|
+
function isElectron() {
|
|
22764
|
+
if (process.versions && process.versions.electron) return true;
|
|
22765
|
+
if (process.env.ELECTRON_RUN_AS_NODE) return true;
|
|
22766
|
+
return typeof window !== "undefined" && window.process && window.process.type === "renderer";
|
|
22767
|
+
}
|
|
22768
|
+
function isAlpine(platform2) {
|
|
22769
|
+
return platform2 === "linux" && fs.existsSync("/etc/alpine-release");
|
|
22770
|
+
}
|
|
22771
|
+
load.parseTags = parseTags;
|
|
22772
|
+
load.matchTags = matchTags;
|
|
22773
|
+
load.compareTags = compareTags;
|
|
22774
|
+
load.parseTuple = parseTuple;
|
|
22775
|
+
load.matchTuple = matchTuple;
|
|
22776
|
+
load.compareTuples = compareTuples;
|
|
22777
|
+
}
|
|
22778
|
+
});
|
|
22779
|
+
|
|
22780
|
+
// ../../../../node_modules/node-gyp-build/index.js
|
|
22781
|
+
var require_node_gyp_build2 = __commonJS({
|
|
22782
|
+
"../../../../node_modules/node-gyp-build/index.js"(exports, module) {
|
|
22783
|
+
var runtimeRequire = typeof __webpack_require__ === "function" ? __non_webpack_require__ : __require;
|
|
22784
|
+
if (typeof runtimeRequire.addon === "function") {
|
|
22785
|
+
module.exports = runtimeRequire.addon.bind(runtimeRequire);
|
|
22786
|
+
} else {
|
|
22787
|
+
module.exports = require_node_gyp_build();
|
|
22788
|
+
}
|
|
22789
|
+
}
|
|
22790
|
+
});
|
|
22791
|
+
|
|
22792
|
+
// ../../../../node_modules/bufferutil/fallback.js
|
|
22793
|
+
var require_fallback = __commonJS({
|
|
22794
|
+
"../../../../node_modules/bufferutil/fallback.js"(exports, module) {
|
|
22795
|
+
"use strict";
|
|
22796
|
+
var mask = (source, mask2, output, offset, length) => {
|
|
22797
|
+
for (var i = 0; i < length; i++) {
|
|
22798
|
+
output[offset + i] = source[i] ^ mask2[i & 3];
|
|
22799
|
+
}
|
|
22800
|
+
};
|
|
22801
|
+
var unmask = (buffer, mask2) => {
|
|
22802
|
+
const length = buffer.length;
|
|
22803
|
+
for (var i = 0; i < length; i++) {
|
|
22804
|
+
buffer[i] ^= mask2[i & 3];
|
|
22805
|
+
}
|
|
22806
|
+
};
|
|
22807
|
+
module.exports = { mask, unmask };
|
|
22808
|
+
}
|
|
22809
|
+
});
|
|
22810
|
+
|
|
22811
|
+
// ../../../../node_modules/bufferutil/index.js
|
|
22812
|
+
var require_bufferutil = __commonJS({
|
|
22813
|
+
"../../../../node_modules/bufferutil/index.js"(exports, module) {
|
|
22814
|
+
"use strict";
|
|
22815
|
+
try {
|
|
22816
|
+
module.exports = require_node_gyp_build2()(__dirname);
|
|
22817
|
+
} catch (e) {
|
|
22818
|
+
module.exports = require_fallback();
|
|
22819
|
+
}
|
|
22820
|
+
}
|
|
22821
|
+
});
|
|
22822
|
+
|
|
22611
22823
|
// node_modules/ws/lib/buffer-util.js
|
|
22612
22824
|
var require_buffer_util = __commonJS({
|
|
22613
22825
|
"node_modules/ws/lib/buffer-util.js"(exports, module) {
|
|
@@ -22668,7 +22880,7 @@ var require_buffer_util = __commonJS({
|
|
|
22668
22880
|
};
|
|
22669
22881
|
if (!process.env.WS_NO_BUFFER_UTIL) {
|
|
22670
22882
|
try {
|
|
22671
|
-
const bufferUtil =
|
|
22883
|
+
const bufferUtil = require_bufferutil();
|
|
22672
22884
|
module.exports.mask = function(source, mask, output, offset, length) {
|
|
22673
22885
|
if (length < 48) _mask(source, mask, output, offset, length);
|
|
22674
22886
|
else bufferUtil.mask(source, mask, output, offset, length);
|
|
@@ -23116,6 +23328,55 @@ var require_permessage_deflate = __commonJS({
|
|
|
23116
23328
|
}
|
|
23117
23329
|
});
|
|
23118
23330
|
|
|
23331
|
+
// ../../../../node_modules/utf-8-validate/fallback.js
|
|
23332
|
+
var require_fallback2 = __commonJS({
|
|
23333
|
+
"../../../../node_modules/utf-8-validate/fallback.js"(exports, module) {
|
|
23334
|
+
"use strict";
|
|
23335
|
+
function isValidUTF8(buf) {
|
|
23336
|
+
const len = buf.length;
|
|
23337
|
+
let i = 0;
|
|
23338
|
+
while (i < len) {
|
|
23339
|
+
if ((buf[i] & 128) === 0) {
|
|
23340
|
+
i++;
|
|
23341
|
+
} else if ((buf[i] & 224) === 192) {
|
|
23342
|
+
if (i + 1 === len || (buf[i + 1] & 192) !== 128 || (buf[i] & 254) === 192) {
|
|
23343
|
+
return false;
|
|
23344
|
+
}
|
|
23345
|
+
i += 2;
|
|
23346
|
+
} else if ((buf[i] & 240) === 224) {
|
|
23347
|
+
if (i + 2 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || buf[i] === 224 && (buf[i + 1] & 224) === 128 || // overlong
|
|
23348
|
+
buf[i] === 237 && (buf[i + 1] & 224) === 160) {
|
|
23349
|
+
return false;
|
|
23350
|
+
}
|
|
23351
|
+
i += 3;
|
|
23352
|
+
} else if ((buf[i] & 248) === 240) {
|
|
23353
|
+
if (i + 3 >= len || (buf[i + 1] & 192) !== 128 || (buf[i + 2] & 192) !== 128 || (buf[i + 3] & 192) !== 128 || buf[i] === 240 && (buf[i + 1] & 240) === 128 || // overlong
|
|
23354
|
+
buf[i] === 244 && buf[i + 1] > 143 || buf[i] > 244) {
|
|
23355
|
+
return false;
|
|
23356
|
+
}
|
|
23357
|
+
i += 4;
|
|
23358
|
+
} else {
|
|
23359
|
+
return false;
|
|
23360
|
+
}
|
|
23361
|
+
}
|
|
23362
|
+
return true;
|
|
23363
|
+
}
|
|
23364
|
+
module.exports = isValidUTF8;
|
|
23365
|
+
}
|
|
23366
|
+
});
|
|
23367
|
+
|
|
23368
|
+
// ../../../../node_modules/utf-8-validate/index.js
|
|
23369
|
+
var require_utf_8_validate = __commonJS({
|
|
23370
|
+
"../../../../node_modules/utf-8-validate/index.js"(exports, module) {
|
|
23371
|
+
"use strict";
|
|
23372
|
+
try {
|
|
23373
|
+
module.exports = require_node_gyp_build2()(__dirname);
|
|
23374
|
+
} catch (e) {
|
|
23375
|
+
module.exports = require_fallback2();
|
|
23376
|
+
}
|
|
23377
|
+
}
|
|
23378
|
+
});
|
|
23379
|
+
|
|
23119
23380
|
// node_modules/ws/lib/validation.js
|
|
23120
23381
|
var require_validation = __commonJS({
|
|
23121
23382
|
"node_modules/ws/lib/validation.js"(exports, module) {
|
|
@@ -23307,7 +23568,7 @@ var require_validation = __commonJS({
|
|
|
23307
23568
|
};
|
|
23308
23569
|
} else if (!process.env.WS_NO_UTF_8_VALIDATE) {
|
|
23309
23570
|
try {
|
|
23310
|
-
const isValidUTF8 =
|
|
23571
|
+
const isValidUTF8 = require_utf_8_validate();
|
|
23311
23572
|
module.exports.isValidUTF8 = function(buf) {
|
|
23312
23573
|
return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf);
|
|
23313
23574
|
};
|
|
@@ -24793,7 +25054,7 @@ var require_websocket = __commonJS({
|
|
|
24793
25054
|
var http = __require("http");
|
|
24794
25055
|
var net = __require("net");
|
|
24795
25056
|
var tls = __require("tls");
|
|
24796
|
-
var { randomBytes, createHash } = __require("crypto");
|
|
25057
|
+
var { randomBytes, createHash: createHash2 } = __require("crypto");
|
|
24797
25058
|
var { Duplex, Readable } = __require("stream");
|
|
24798
25059
|
var { URL: URL2 } = __require("url");
|
|
24799
25060
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -25453,7 +25714,7 @@ var require_websocket = __commonJS({
|
|
|
25453
25714
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
25454
25715
|
return;
|
|
25455
25716
|
}
|
|
25456
|
-
const digest =
|
|
25717
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
25457
25718
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
25458
25719
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
25459
25720
|
return;
|
|
@@ -25732,7 +25993,7 @@ var require_stream = __commonJS({
|
|
|
25732
25993
|
};
|
|
25733
25994
|
duplex._final = function(callback) {
|
|
25734
25995
|
if (ws.readyState === ws.CONNECTING) {
|
|
25735
|
-
ws.once("open", function
|
|
25996
|
+
ws.once("open", function open2() {
|
|
25736
25997
|
duplex._final(callback);
|
|
25737
25998
|
});
|
|
25738
25999
|
return;
|
|
@@ -25753,7 +26014,7 @@ var require_stream = __commonJS({
|
|
|
25753
26014
|
};
|
|
25754
26015
|
duplex._write = function(chunk, encoding, callback) {
|
|
25755
26016
|
if (ws.readyState === ws.CONNECTING) {
|
|
25756
|
-
ws.once("open", function
|
|
26017
|
+
ws.once("open", function open2() {
|
|
25757
26018
|
duplex._write(chunk, encoding, callback);
|
|
25758
26019
|
});
|
|
25759
26020
|
return;
|
|
@@ -25820,7 +26081,7 @@ var require_websocket_server = __commonJS({
|
|
|
25820
26081
|
var EventEmitter = __require("events");
|
|
25821
26082
|
var http = __require("http");
|
|
25822
26083
|
var { Duplex } = __require("stream");
|
|
25823
|
-
var { createHash } = __require("crypto");
|
|
26084
|
+
var { createHash: createHash2 } = __require("crypto");
|
|
25824
26085
|
var extension2 = require_extension();
|
|
25825
26086
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
25826
26087
|
var subprotocol2 = require_subprotocol();
|
|
@@ -26121,7 +26382,7 @@ var require_websocket_server = __commonJS({
|
|
|
26121
26382
|
);
|
|
26122
26383
|
}
|
|
26123
26384
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
26124
|
-
const digest =
|
|
26385
|
+
const digest = createHash2("sha1").update(key + GUID).digest("base64");
|
|
26125
26386
|
const headers = [
|
|
26126
26387
|
"HTTP/1.1 101 Switching Protocols",
|
|
26127
26388
|
"Upgrade: websocket",
|
|
@@ -27261,8 +27522,8 @@ var A2AServer = class {
|
|
|
27261
27522
|
const task = response.result;
|
|
27262
27523
|
if (task?.id && !isTerminalTaskState(task.status?.state)) {
|
|
27263
27524
|
const terminal = await this.waitForLocalTaskTerminal(task.id).catch((err) => {
|
|
27264
|
-
const
|
|
27265
|
-
this.logger.info(`[perkos-a2a] Waiting for relay task ${task.id} terminal state failed: ${
|
|
27525
|
+
const errMsg2 = err instanceof Error ? err.message : String(err);
|
|
27526
|
+
this.logger.info(`[perkos-a2a] Waiting for relay task ${task.id} terminal state failed: ${errMsg2}`);
|
|
27266
27527
|
return null;
|
|
27267
27528
|
});
|
|
27268
27529
|
if (terminal) {
|
|
@@ -27272,9 +27533,9 @@ var A2AServer = class {
|
|
|
27272
27533
|
}
|
|
27273
27534
|
this.relayClient?.sendTaskResponse(msg, response);
|
|
27274
27535
|
}).catch((err) => {
|
|
27275
|
-
const
|
|
27276
|
-
this.logger.error(`[perkos-a2a] Failed to process relay task: ${
|
|
27277
|
-
this.relayClient?.sendTaskResponse(msg, this.error(rpcId, -32603,
|
|
27536
|
+
const errMsg2 = err instanceof Error ? err.message : String(err);
|
|
27537
|
+
this.logger.error(`[perkos-a2a] Failed to process relay task: ${errMsg2}`);
|
|
27538
|
+
this.relayClient?.sendTaskResponse(msg, this.error(rpcId, -32603, errMsg2));
|
|
27278
27539
|
});
|
|
27279
27540
|
}
|
|
27280
27541
|
startRelayClient() {
|
|
@@ -27390,7 +27651,7 @@ var A2AServer = class {
|
|
|
27390
27651
|
};
|
|
27391
27652
|
|
|
27392
27653
|
// src/index.ts
|
|
27393
|
-
import { randomUUID as
|
|
27654
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
27394
27655
|
|
|
27395
27656
|
// src/agentic-actions.ts
|
|
27396
27657
|
var FALLBACK_AGENT_ALIASES = [
|
|
@@ -27805,6 +28066,550 @@ ${replyResultText2}` : void 0,
|
|
|
27805
28066
|
return true;
|
|
27806
28067
|
}
|
|
27807
28068
|
|
|
28069
|
+
// src/chat-client.ts
|
|
28070
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
28071
|
+
|
|
28072
|
+
// src/chat-store.ts
|
|
28073
|
+
import { createHash } from "node:crypto";
|
|
28074
|
+
import { createReadStream } from "node:fs";
|
|
28075
|
+
import {
|
|
28076
|
+
appendFile,
|
|
28077
|
+
mkdir,
|
|
28078
|
+
open,
|
|
28079
|
+
readFile,
|
|
28080
|
+
writeFile
|
|
28081
|
+
} from "node:fs/promises";
|
|
28082
|
+
import { homedir as homedir2 } from "node:os";
|
|
28083
|
+
import { join } from "node:path";
|
|
28084
|
+
function safeConvId(convId) {
|
|
28085
|
+
if (!/^[A-Za-z0-9_-]{1,128}$/.test(convId)) {
|
|
28086
|
+
throw new Error(`invalid convId: ${convId}`);
|
|
28087
|
+
}
|
|
28088
|
+
return convId;
|
|
28089
|
+
}
|
|
28090
|
+
var ChatStore = class {
|
|
28091
|
+
root;
|
|
28092
|
+
constructor(options = {}) {
|
|
28093
|
+
this.root = options.storeRoot ?? join(homedir2(), ".perkos", "conversations");
|
|
28094
|
+
}
|
|
28095
|
+
/** Directory for a single conversation. */
|
|
28096
|
+
dirFor(convId) {
|
|
28097
|
+
return join(this.root, safeConvId(convId));
|
|
28098
|
+
}
|
|
28099
|
+
/** Path to the JSONL log for a conv. */
|
|
28100
|
+
jsonlPath(convId) {
|
|
28101
|
+
return join(this.dirFor(convId), "messages.jsonl");
|
|
28102
|
+
}
|
|
28103
|
+
/** Path to the metadata file for a conv. */
|
|
28104
|
+
metaPath(convId) {
|
|
28105
|
+
return join(this.dirFor(convId), "metadata.json");
|
|
28106
|
+
}
|
|
28107
|
+
/** Ensure the conv directory exists. Idempotent. */
|
|
28108
|
+
async ensureDir(convId) {
|
|
28109
|
+
await mkdir(this.dirFor(convId), { recursive: true });
|
|
28110
|
+
}
|
|
28111
|
+
/**
|
|
28112
|
+
* Write metadata for a conversation. Called on `channel_join` so the agent
|
|
28113
|
+
* has a local record of which participants belong to a conv.
|
|
28114
|
+
*/
|
|
28115
|
+
async writeMetadata(meta) {
|
|
28116
|
+
await this.ensureDir(meta.convId);
|
|
28117
|
+
await writeFile(this.metaPath(meta.convId), JSON.stringify(meta, null, 2));
|
|
28118
|
+
}
|
|
28119
|
+
async readMetadata(convId) {
|
|
28120
|
+
try {
|
|
28121
|
+
const raw = await readFile(this.metaPath(convId), "utf8");
|
|
28122
|
+
return JSON.parse(raw);
|
|
28123
|
+
} catch (err) {
|
|
28124
|
+
if (isNotFound(err)) return null;
|
|
28125
|
+
throw err;
|
|
28126
|
+
}
|
|
28127
|
+
}
|
|
28128
|
+
/**
|
|
28129
|
+
* Append a message to the conversation log. The line written is exactly
|
|
28130
|
+
* `JSON.stringify(msg) + "\n"`. The store does not validate ordering; if
|
|
28131
|
+
* messages arrive out of timestamp order, that is recorded as-is.
|
|
28132
|
+
*/
|
|
28133
|
+
async append(convId, msg) {
|
|
28134
|
+
await this.ensureDir(convId);
|
|
28135
|
+
await appendFile(this.jsonlPath(convId), JSON.stringify(msg) + "\n");
|
|
28136
|
+
}
|
|
28137
|
+
/**
|
|
28138
|
+
* Read a history page, reverse-chronological. Returns messages with
|
|
28139
|
+
* timestamp strictly less than `before` (if provided), up to `limit`.
|
|
28140
|
+
*
|
|
28141
|
+
* The output order is chronological ascending — i.e. callers can `concat`
|
|
28142
|
+
* pages from oldest to newest without sorting.
|
|
28143
|
+
*/
|
|
28144
|
+
async readPage(convId, opts) {
|
|
28145
|
+
const limit = Math.max(1, Math.min(500, opts.limit));
|
|
28146
|
+
const before = opts.before ?? null;
|
|
28147
|
+
let lines;
|
|
28148
|
+
try {
|
|
28149
|
+
const raw = await readFile(this.jsonlPath(convId), "utf8");
|
|
28150
|
+
lines = raw.split("\n").filter((l) => l.length > 0);
|
|
28151
|
+
} catch (err) {
|
|
28152
|
+
if (isNotFound(err)) return { messages: [], hasMore: false };
|
|
28153
|
+
throw err;
|
|
28154
|
+
}
|
|
28155
|
+
const collected = [];
|
|
28156
|
+
let hasMore = false;
|
|
28157
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
28158
|
+
let msg;
|
|
28159
|
+
try {
|
|
28160
|
+
msg = JSON.parse(lines[i]);
|
|
28161
|
+
} catch {
|
|
28162
|
+
continue;
|
|
28163
|
+
}
|
|
28164
|
+
if (before && msg.timestamp >= before) continue;
|
|
28165
|
+
if (collected.length >= limit) {
|
|
28166
|
+
hasMore = true;
|
|
28167
|
+
break;
|
|
28168
|
+
}
|
|
28169
|
+
collected.push(msg);
|
|
28170
|
+
}
|
|
28171
|
+
return { messages: collected.reverse(), hasMore };
|
|
28172
|
+
}
|
|
28173
|
+
/**
|
|
28174
|
+
* Produce a tamper-evident summary of the conversation log for receipt
|
|
28175
|
+
* issuance. Streams `messages.jsonl` through sha256 (followed by a
|
|
28176
|
+
* separator + metadata.json content if present), so memory usage stays
|
|
28177
|
+
* constant regardless of conv size.
|
|
28178
|
+
*
|
|
28179
|
+
* The hash is deterministic: same jsonl bytes → same hex string. A
|
|
28180
|
+
* downstream verifier with the jsonl can recompute and confirm.
|
|
28181
|
+
*
|
|
28182
|
+
* Returns counts + first/last timestamps for the receipt manifest.
|
|
28183
|
+
*/
|
|
28184
|
+
async computeReceipt(convId) {
|
|
28185
|
+
const path = this.jsonlPath(convId);
|
|
28186
|
+
try {
|
|
28187
|
+
const fd = await open(path, "r");
|
|
28188
|
+
await fd.close();
|
|
28189
|
+
} catch (err) {
|
|
28190
|
+
if (isNotFound(err)) return null;
|
|
28191
|
+
throw err;
|
|
28192
|
+
}
|
|
28193
|
+
const stream = createReadStream(path, { encoding: "utf8", highWaterMark: 64 * 1024 });
|
|
28194
|
+
const hash = createHash("sha256");
|
|
28195
|
+
let messageCount = 0;
|
|
28196
|
+
let firstMessageAt = null;
|
|
28197
|
+
let lastMessageAt = null;
|
|
28198
|
+
let leftover = "";
|
|
28199
|
+
return new Promise((resolve, reject) => {
|
|
28200
|
+
stream.on("data", (chunk) => {
|
|
28201
|
+
const text = typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
28202
|
+
hash.update(text);
|
|
28203
|
+
const combined = leftover + text;
|
|
28204
|
+
const lines = combined.split("\n");
|
|
28205
|
+
leftover = lines.pop() ?? "";
|
|
28206
|
+
for (const line of lines) {
|
|
28207
|
+
if (!line) continue;
|
|
28208
|
+
messageCount++;
|
|
28209
|
+
try {
|
|
28210
|
+
const parsed = JSON.parse(line);
|
|
28211
|
+
const ts = parsed.timestamp;
|
|
28212
|
+
if (typeof ts === "string") {
|
|
28213
|
+
if (firstMessageAt === null || ts < firstMessageAt) firstMessageAt = ts;
|
|
28214
|
+
if (lastMessageAt === null || ts > lastMessageAt) lastMessageAt = ts;
|
|
28215
|
+
}
|
|
28216
|
+
} catch {
|
|
28217
|
+
}
|
|
28218
|
+
}
|
|
28219
|
+
});
|
|
28220
|
+
stream.on("end", async () => {
|
|
28221
|
+
if (leftover.length > 0) {
|
|
28222
|
+
messageCount++;
|
|
28223
|
+
try {
|
|
28224
|
+
const parsed = JSON.parse(leftover);
|
|
28225
|
+
const ts = parsed.timestamp;
|
|
28226
|
+
if (typeof ts === "string") {
|
|
28227
|
+
if (firstMessageAt === null || ts < firstMessageAt) firstMessageAt = ts;
|
|
28228
|
+
if (lastMessageAt === null || ts > lastMessageAt) lastMessageAt = ts;
|
|
28229
|
+
}
|
|
28230
|
+
} catch {
|
|
28231
|
+
}
|
|
28232
|
+
}
|
|
28233
|
+
try {
|
|
28234
|
+
const metaRaw = await readFile(this.metaPath(convId), "utf8");
|
|
28235
|
+
hash.update("");
|
|
28236
|
+
hash.update(metaRaw);
|
|
28237
|
+
} catch {
|
|
28238
|
+
}
|
|
28239
|
+
resolve({
|
|
28240
|
+
transcriptHash: hash.digest("hex"),
|
|
28241
|
+
hashAlgo: "sha256",
|
|
28242
|
+
messageCount,
|
|
28243
|
+
firstMessageAt,
|
|
28244
|
+
lastMessageAt
|
|
28245
|
+
});
|
|
28246
|
+
});
|
|
28247
|
+
stream.on("error", (err) => reject(err));
|
|
28248
|
+
});
|
|
28249
|
+
}
|
|
28250
|
+
/**
|
|
28251
|
+
* Return the number of messages in the log. Useful for tests and stats.
|
|
28252
|
+
* Avoid using this on the hot path — it reads the full file.
|
|
28253
|
+
*/
|
|
28254
|
+
async count(convId) {
|
|
28255
|
+
let fd;
|
|
28256
|
+
try {
|
|
28257
|
+
fd = await open(this.jsonlPath(convId), "r");
|
|
28258
|
+
} catch (err) {
|
|
28259
|
+
if (isNotFound(err)) return 0;
|
|
28260
|
+
throw err;
|
|
28261
|
+
}
|
|
28262
|
+
try {
|
|
28263
|
+
const raw = await fd.readFile({ encoding: "utf8" });
|
|
28264
|
+
return raw.split("\n").filter((l) => l.length > 0).length;
|
|
28265
|
+
} finally {
|
|
28266
|
+
await fd.close();
|
|
28267
|
+
}
|
|
28268
|
+
}
|
|
28269
|
+
};
|
|
28270
|
+
function isNotFound(err) {
|
|
28271
|
+
return !!err && typeof err === "object" && err.code === "ENOENT";
|
|
28272
|
+
}
|
|
28273
|
+
|
|
28274
|
+
// src/chat-client.ts
|
|
28275
|
+
var DEFAULT_URL = "wss://chat.perkos.xyz/chat";
|
|
28276
|
+
var DEFAULT_HISTORY_LIMIT = 50;
|
|
28277
|
+
var DEFAULT_MIN_RECONNECT = 1e3;
|
|
28278
|
+
var DEFAULT_MAX_RECONNECT = 6e4;
|
|
28279
|
+
var DEFAULT_HEARTBEAT_MS = 25e3;
|
|
28280
|
+
var AUTH_TIMEOUT_MS = 1e4;
|
|
28281
|
+
var ChatClient = class {
|
|
28282
|
+
ws = null;
|
|
28283
|
+
config;
|
|
28284
|
+
agentName;
|
|
28285
|
+
handlers;
|
|
28286
|
+
logger;
|
|
28287
|
+
store;
|
|
28288
|
+
reconnectMs;
|
|
28289
|
+
reconnectTimer = null;
|
|
28290
|
+
heartbeatTimer = null;
|
|
28291
|
+
connected = false;
|
|
28292
|
+
authed = false;
|
|
28293
|
+
stopped = false;
|
|
28294
|
+
constructor(opts) {
|
|
28295
|
+
this.agentName = opts.agentName;
|
|
28296
|
+
const cfg = opts.config;
|
|
28297
|
+
this.config = {
|
|
28298
|
+
enabled: cfg.enabled,
|
|
28299
|
+
url: cfg.url || DEFAULT_URL,
|
|
28300
|
+
apiKey: cfg.apiKey,
|
|
28301
|
+
storeRoot: cfg.storeRoot,
|
|
28302
|
+
defaultHistoryLimit: cfg.defaultHistoryLimit ?? DEFAULT_HISTORY_LIMIT,
|
|
28303
|
+
minReconnectMs: cfg.minReconnectMs ?? DEFAULT_MIN_RECONNECT,
|
|
28304
|
+
maxReconnectMs: cfg.maxReconnectMs ?? DEFAULT_MAX_RECONNECT,
|
|
28305
|
+
heartbeatIntervalMs: cfg.heartbeatIntervalMs ?? DEFAULT_HEARTBEAT_MS
|
|
28306
|
+
};
|
|
28307
|
+
this.handlers = opts.handlers ?? {};
|
|
28308
|
+
this.logger = this.handlers.logger ?? { info: console.log, error: console.error };
|
|
28309
|
+
this.store = new ChatStore({ storeRoot: this.config.storeRoot });
|
|
28310
|
+
this.reconnectMs = this.config.minReconnectMs;
|
|
28311
|
+
}
|
|
28312
|
+
/** Storage handle — exposed so consumers can read history without going through the wire. */
|
|
28313
|
+
getStore() {
|
|
28314
|
+
return this.store;
|
|
28315
|
+
}
|
|
28316
|
+
isConnected() {
|
|
28317
|
+
return this.connected && this.authed;
|
|
28318
|
+
}
|
|
28319
|
+
start() {
|
|
28320
|
+
if (!this.config.enabled) {
|
|
28321
|
+
this.logger.info("[perkos-chat] disabled in config; not connecting");
|
|
28322
|
+
return;
|
|
28323
|
+
}
|
|
28324
|
+
if (!this.config.apiKey) {
|
|
28325
|
+
this.logger.error("[perkos-chat] missing apiKey; cannot connect");
|
|
28326
|
+
return;
|
|
28327
|
+
}
|
|
28328
|
+
this.stopped = false;
|
|
28329
|
+
this.connect();
|
|
28330
|
+
}
|
|
28331
|
+
stop() {
|
|
28332
|
+
this.stopped = true;
|
|
28333
|
+
this.clearTimers();
|
|
28334
|
+
if (this.ws) {
|
|
28335
|
+
try {
|
|
28336
|
+
this.ws.close(1e3, "client shutting down");
|
|
28337
|
+
} catch {
|
|
28338
|
+
}
|
|
28339
|
+
this.ws = null;
|
|
28340
|
+
}
|
|
28341
|
+
this.connected = false;
|
|
28342
|
+
this.authed = false;
|
|
28343
|
+
}
|
|
28344
|
+
/**
|
|
28345
|
+
* Send a reply from this agent into a conversation. Appends to the local
|
|
28346
|
+
* store optimistically, then sends to the server. If the WS is offline,
|
|
28347
|
+
* the local store is still updated and the message is dropped from the
|
|
28348
|
+
* wire — callers should re-send after reconnect if guaranteed delivery is
|
|
28349
|
+
* required (the simple agent loop should just regenerate from history).
|
|
28350
|
+
*/
|
|
28351
|
+
async sendReply(opts) {
|
|
28352
|
+
const id = randomUUID5();
|
|
28353
|
+
const msg = {
|
|
28354
|
+
id,
|
|
28355
|
+
from: `agent:${this.agentName}`,
|
|
28356
|
+
text: opts.text,
|
|
28357
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28358
|
+
replyTo: opts.replyTo ?? null
|
|
28359
|
+
};
|
|
28360
|
+
await this.store.append(opts.convId, msg);
|
|
28361
|
+
const frame = {
|
|
28362
|
+
type: "chat_reply",
|
|
28363
|
+
id,
|
|
28364
|
+
convId: opts.convId,
|
|
28365
|
+
walletAddress: opts.walletAddress,
|
|
28366
|
+
text: opts.text,
|
|
28367
|
+
replyTo: opts.replyTo ?? null
|
|
28368
|
+
};
|
|
28369
|
+
const delivered = this.sendFrame(frame);
|
|
28370
|
+
return { id, delivered };
|
|
28371
|
+
}
|
|
28372
|
+
// -------------------------------------------------------------------------
|
|
28373
|
+
// WebSocket lifecycle
|
|
28374
|
+
// -------------------------------------------------------------------------
|
|
28375
|
+
connect() {
|
|
28376
|
+
if (this.stopped) return;
|
|
28377
|
+
const url = this.config.url;
|
|
28378
|
+
this.logger.info(`[perkos-chat] connecting to ${url}`);
|
|
28379
|
+
try {
|
|
28380
|
+
this.ws = new wrapper_default(url);
|
|
28381
|
+
} catch (err) {
|
|
28382
|
+
this.logger.error(`[perkos-chat] failed to construct WS: ${errMsg(err)}`);
|
|
28383
|
+
this.scheduleReconnect();
|
|
28384
|
+
return;
|
|
28385
|
+
}
|
|
28386
|
+
let authTimer = setTimeout(() => {
|
|
28387
|
+
if (!this.authed) {
|
|
28388
|
+
this.logger.error("[perkos-chat] auth timeout");
|
|
28389
|
+
try {
|
|
28390
|
+
this.ws?.close(4002, "auth timeout");
|
|
28391
|
+
} catch {
|
|
28392
|
+
}
|
|
28393
|
+
}
|
|
28394
|
+
}, AUTH_TIMEOUT_MS);
|
|
28395
|
+
this.ws.on("open", () => {
|
|
28396
|
+
this.connected = true;
|
|
28397
|
+
this.reconnectMs = this.config.minReconnectMs;
|
|
28398
|
+
this.sendFrame({
|
|
28399
|
+
type: "auth",
|
|
28400
|
+
role: "agent",
|
|
28401
|
+
agentName: this.agentName,
|
|
28402
|
+
apiKey: this.config.apiKey
|
|
28403
|
+
});
|
|
28404
|
+
});
|
|
28405
|
+
this.ws.on("message", (data) => {
|
|
28406
|
+
let frame;
|
|
28407
|
+
try {
|
|
28408
|
+
frame = JSON.parse(data.toString("utf8"));
|
|
28409
|
+
} catch {
|
|
28410
|
+
this.logger.error("[perkos-chat] failed to parse frame");
|
|
28411
|
+
return;
|
|
28412
|
+
}
|
|
28413
|
+
if (authTimer) {
|
|
28414
|
+
clearTimeout(authTimer);
|
|
28415
|
+
authTimer = null;
|
|
28416
|
+
}
|
|
28417
|
+
void this.handleFrame(frame);
|
|
28418
|
+
});
|
|
28419
|
+
this.ws.on("close", (code, reason) => {
|
|
28420
|
+
this.connected = false;
|
|
28421
|
+
this.authed = false;
|
|
28422
|
+
this.clearTimers();
|
|
28423
|
+
if (authTimer) {
|
|
28424
|
+
clearTimeout(authTimer);
|
|
28425
|
+
authTimer = null;
|
|
28426
|
+
}
|
|
28427
|
+
if (!this.stopped) {
|
|
28428
|
+
this.logger.info(`[perkos-chat] disconnected (${code}: ${reason?.toString() || "no reason"}); reconnecting`);
|
|
28429
|
+
this.scheduleReconnect();
|
|
28430
|
+
}
|
|
28431
|
+
});
|
|
28432
|
+
this.ws.on("error", (err) => {
|
|
28433
|
+
this.logger.error(`[perkos-chat] WS error: ${errMsg(err)}`);
|
|
28434
|
+
});
|
|
28435
|
+
}
|
|
28436
|
+
async handleFrame(frame) {
|
|
28437
|
+
switch (frame.type) {
|
|
28438
|
+
case "auth_ok":
|
|
28439
|
+
this.authed = true;
|
|
28440
|
+
this.logger.info(`[perkos-chat] authed as agent:${this.agentName}`);
|
|
28441
|
+
this.startHeartbeat();
|
|
28442
|
+
return;
|
|
28443
|
+
case "auth_error":
|
|
28444
|
+
this.logger.error(`[perkos-chat] auth_error ${frame.code}: ${frame.message}`);
|
|
28445
|
+
try {
|
|
28446
|
+
this.ws?.close(4401, frame.code);
|
|
28447
|
+
} catch {
|
|
28448
|
+
}
|
|
28449
|
+
return;
|
|
28450
|
+
case "chat_deliver":
|
|
28451
|
+
case "chat_message": {
|
|
28452
|
+
await this.persistInbound(frame);
|
|
28453
|
+
if (this.handlers.onChatDeliver) {
|
|
28454
|
+
try {
|
|
28455
|
+
await this.handlers.onChatDeliver(frame);
|
|
28456
|
+
} catch (err) {
|
|
28457
|
+
this.logger.error(`[perkos-chat] onChatDeliver threw: ${errMsg(err)}`);
|
|
28458
|
+
}
|
|
28459
|
+
}
|
|
28460
|
+
return;
|
|
28461
|
+
}
|
|
28462
|
+
case "history_request":
|
|
28463
|
+
await this.respondToHistoryRequest(frame);
|
|
28464
|
+
return;
|
|
28465
|
+
case "receipt_request":
|
|
28466
|
+
await this.respondToReceiptRequest(frame);
|
|
28467
|
+
return;
|
|
28468
|
+
case "channel_join":
|
|
28469
|
+
await this.handleChannelJoin(frame);
|
|
28470
|
+
return;
|
|
28471
|
+
case "ack":
|
|
28472
|
+
case "pong":
|
|
28473
|
+
case "typing":
|
|
28474
|
+
return;
|
|
28475
|
+
case "error":
|
|
28476
|
+
this.logger.error(`[perkos-chat] server error ${frame.code}: ${frame.message}`);
|
|
28477
|
+
return;
|
|
28478
|
+
default:
|
|
28479
|
+
this.logger.info(`[perkos-chat] unknown frame type: ${frame.type}`);
|
|
28480
|
+
}
|
|
28481
|
+
}
|
|
28482
|
+
async persistInbound(frame) {
|
|
28483
|
+
const msg = {
|
|
28484
|
+
id: frame.id,
|
|
28485
|
+
from: frame.from,
|
|
28486
|
+
text: frame.text,
|
|
28487
|
+
timestamp: frame.timestamp,
|
|
28488
|
+
replyTo: "replyTo" in frame ? frame.replyTo ?? null : null
|
|
28489
|
+
};
|
|
28490
|
+
try {
|
|
28491
|
+
await this.store.append(frame.convId, msg);
|
|
28492
|
+
} catch (err) {
|
|
28493
|
+
this.logger.error(`[perkos-chat] failed to append to store: ${errMsg(err)}`);
|
|
28494
|
+
}
|
|
28495
|
+
}
|
|
28496
|
+
async handleChannelJoin(frame) {
|
|
28497
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
28498
|
+
const existing = await this.store.readMetadata(frame.convId);
|
|
28499
|
+
const meta = {
|
|
28500
|
+
convId: frame.convId,
|
|
28501
|
+
participants: frame.participants,
|
|
28502
|
+
historyHost: frame.historyHost,
|
|
28503
|
+
createdAt: existing?.createdAt ?? now,
|
|
28504
|
+
updatedAt: now
|
|
28505
|
+
};
|
|
28506
|
+
try {
|
|
28507
|
+
await this.store.writeMetadata(meta);
|
|
28508
|
+
} catch (err) {
|
|
28509
|
+
this.logger.error(`[perkos-chat] failed to write metadata for ${frame.convId}: ${errMsg(err)}`);
|
|
28510
|
+
}
|
|
28511
|
+
if (this.handlers.onChannelJoin) {
|
|
28512
|
+
try {
|
|
28513
|
+
await this.handlers.onChannelJoin(frame);
|
|
28514
|
+
} catch (err) {
|
|
28515
|
+
this.logger.error(`[perkos-chat] onChannelJoin threw: ${errMsg(err)}`);
|
|
28516
|
+
}
|
|
28517
|
+
}
|
|
28518
|
+
}
|
|
28519
|
+
async respondToReceiptRequest(frame) {
|
|
28520
|
+
let summary;
|
|
28521
|
+
try {
|
|
28522
|
+
summary = await this.store.computeReceipt(frame.convId);
|
|
28523
|
+
} catch (err) {
|
|
28524
|
+
this.logger.error(`[perkos-chat] receipt computation failed: ${errMsg(err)}`);
|
|
28525
|
+
return;
|
|
28526
|
+
}
|
|
28527
|
+
if (!summary) {
|
|
28528
|
+
this.sendFrame({
|
|
28529
|
+
type: "receipt_response",
|
|
28530
|
+
id: frame.id,
|
|
28531
|
+
convId: frame.convId,
|
|
28532
|
+
forWallet: frame.forWallet,
|
|
28533
|
+
transcriptHash: "",
|
|
28534
|
+
hashAlgo: "sha256",
|
|
28535
|
+
messageCount: 0,
|
|
28536
|
+
firstMessageAt: null,
|
|
28537
|
+
lastMessageAt: null,
|
|
28538
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
28539
|
+
});
|
|
28540
|
+
return;
|
|
28541
|
+
}
|
|
28542
|
+
this.sendFrame({
|
|
28543
|
+
type: "receipt_response",
|
|
28544
|
+
id: frame.id,
|
|
28545
|
+
convId: frame.convId,
|
|
28546
|
+
forWallet: frame.forWallet,
|
|
28547
|
+
transcriptHash: summary.transcriptHash,
|
|
28548
|
+
hashAlgo: summary.hashAlgo,
|
|
28549
|
+
messageCount: summary.messageCount,
|
|
28550
|
+
firstMessageAt: summary.firstMessageAt,
|
|
28551
|
+
lastMessageAt: summary.lastMessageAt,
|
|
28552
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
28553
|
+
});
|
|
28554
|
+
}
|
|
28555
|
+
async respondToHistoryRequest(frame) {
|
|
28556
|
+
const limit = frame.limit ?? this.config.defaultHistoryLimit;
|
|
28557
|
+
let page;
|
|
28558
|
+
try {
|
|
28559
|
+
page = await this.store.readPage(frame.convId, { before: frame.before ?? null, limit });
|
|
28560
|
+
} catch (err) {
|
|
28561
|
+
this.logger.error(`[perkos-chat] history read failed: ${errMsg(err)}`);
|
|
28562
|
+
page = { messages: [], hasMore: false };
|
|
28563
|
+
}
|
|
28564
|
+
this.sendFrame({
|
|
28565
|
+
type: "history_chunk",
|
|
28566
|
+
id: frame.id,
|
|
28567
|
+
convId: frame.convId,
|
|
28568
|
+
forWallet: frame.forWallet,
|
|
28569
|
+
messages: page.messages,
|
|
28570
|
+
hasMore: page.hasMore
|
|
28571
|
+
});
|
|
28572
|
+
}
|
|
28573
|
+
sendFrame(frame) {
|
|
28574
|
+
if (this.ws && this.ws.readyState === wrapper_default.OPEN) {
|
|
28575
|
+
this.ws.send(JSON.stringify(frame));
|
|
28576
|
+
return true;
|
|
28577
|
+
}
|
|
28578
|
+
return false;
|
|
28579
|
+
}
|
|
28580
|
+
startHeartbeat() {
|
|
28581
|
+
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
28582
|
+
this.heartbeatTimer = setInterval(() => {
|
|
28583
|
+
if (!this.sendFrame({ type: "ping" })) {
|
|
28584
|
+
}
|
|
28585
|
+
}, this.config.heartbeatIntervalMs);
|
|
28586
|
+
}
|
|
28587
|
+
scheduleReconnect() {
|
|
28588
|
+
if (this.stopped) return;
|
|
28589
|
+
const delay = this.reconnectMs;
|
|
28590
|
+
this.reconnectTimer = setTimeout(() => this.connect(), delay);
|
|
28591
|
+
this.reconnectMs = Math.min(delay * 2, this.config.maxReconnectMs);
|
|
28592
|
+
}
|
|
28593
|
+
clearTimers() {
|
|
28594
|
+
if (this.heartbeatTimer) {
|
|
28595
|
+
clearInterval(this.heartbeatTimer);
|
|
28596
|
+
this.heartbeatTimer = null;
|
|
28597
|
+
}
|
|
28598
|
+
if (this.reconnectTimer) {
|
|
28599
|
+
clearTimeout(this.reconnectTimer);
|
|
28600
|
+
this.reconnectTimer = null;
|
|
28601
|
+
}
|
|
28602
|
+
}
|
|
28603
|
+
};
|
|
28604
|
+
function errMsg(err) {
|
|
28605
|
+
return err instanceof Error ? err.message : String(err);
|
|
28606
|
+
}
|
|
28607
|
+
|
|
28608
|
+
// src/types.ts
|
|
28609
|
+
function isWebhookEvent(msg) {
|
|
28610
|
+
return msg.type === "webhook_event";
|
|
28611
|
+
}
|
|
28612
|
+
|
|
27808
28613
|
// src/index.ts
|
|
27809
28614
|
function wakeGatewayAgent(requestHeartbeatNow, reason, logger, sessionKey = "agent:main") {
|
|
27810
28615
|
if (typeof requestHeartbeatNow === "function") {
|
|
@@ -27827,6 +28632,11 @@ function openclawEventSessionKey(config) {
|
|
|
27827
28632
|
if (!key) return "agent:main:main";
|
|
27828
28633
|
return key.endsWith(":main") ? key : `${key}:main`;
|
|
27829
28634
|
}
|
|
28635
|
+
function parseWalletFromIdentity(identity) {
|
|
28636
|
+
if (typeof identity !== "string" || !identity.startsWith("user:")) return null;
|
|
28637
|
+
const addr = identity.slice("user:".length);
|
|
28638
|
+
return addr.startsWith("0x") ? addr.toLowerCase() : null;
|
|
28639
|
+
}
|
|
27830
28640
|
async function deliverToHermes(config, message, logger) {
|
|
27831
28641
|
const runtime = config.runtime || {};
|
|
27832
28642
|
const baseUrl = (runtime.hermesUrl || "http://127.0.0.1:8642").replace(/\/+$/, "");
|
|
@@ -27916,7 +28726,7 @@ function register(api) {
|
|
|
27916
28726
|
await deliverToHermes(pluginConfig, eventText, logger);
|
|
27917
28727
|
task.artifacts.push({
|
|
27918
28728
|
kind: "artifact",
|
|
27919
|
-
artifactId:
|
|
28729
|
+
artifactId: randomUUID6(),
|
|
27920
28730
|
parts: [{ kind: "text", text: `debug: delivered task ${task.id} to Hermes API Server` }]
|
|
27921
28731
|
});
|
|
27922
28732
|
const finalText2 = await generateRuntimeReply(pluginConfig, text, marker);
|
|
@@ -27926,14 +28736,14 @@ function register(api) {
|
|
|
27926
28736
|
if (runtimeKind === "none") {
|
|
27927
28737
|
task.artifacts.push({
|
|
27928
28738
|
kind: "artifact",
|
|
27929
|
-
artifactId:
|
|
28739
|
+
artifactId: randomUUID6(),
|
|
27930
28740
|
parts: [{ kind: "text", text: `debug: runtime delivery disabled for ${marker}` }]
|
|
27931
28741
|
});
|
|
27932
28742
|
return;
|
|
27933
28743
|
}
|
|
27934
28744
|
task.artifacts.push({
|
|
27935
28745
|
kind: "artifact",
|
|
27936
|
-
artifactId:
|
|
28746
|
+
artifactId: randomUUID6(),
|
|
27937
28747
|
parts: [{ kind: "text", text: `debug: enqueued system event for ${marker}` }]
|
|
27938
28748
|
});
|
|
27939
28749
|
if (enqueueSystemEvent) {
|
|
@@ -28017,7 +28827,7 @@ function register(api) {
|
|
|
28017
28827
|
if (!finalText) continue;
|
|
28018
28828
|
task.artifacts.push({
|
|
28019
28829
|
kind: "artifact",
|
|
28020
|
-
artifactId:
|
|
28830
|
+
artifactId: randomUUID6(),
|
|
28021
28831
|
parts: [{ kind: "text", text: finalText }]
|
|
28022
28832
|
});
|
|
28023
28833
|
task.status = {
|
|
@@ -28041,6 +28851,83 @@ function register(api) {
|
|
|
28041
28851
|
logger.info("[perkos-a2a] A2A server stopping");
|
|
28042
28852
|
}
|
|
28043
28853
|
});
|
|
28854
|
+
const chatApiKey = pluginConfig.chat?.apiKey || pluginConfig.relay?.apiKey;
|
|
28855
|
+
const chatClient = pluginConfig.chat?.enabled && chatApiKey ? new ChatClient({
|
|
28856
|
+
agentName: pluginConfig.agentName,
|
|
28857
|
+
config: {
|
|
28858
|
+
enabled: true,
|
|
28859
|
+
url: pluginConfig.chat?.url || "wss://chat.perkos.xyz/chat",
|
|
28860
|
+
apiKey: chatApiKey,
|
|
28861
|
+
storeRoot: pluginConfig.chat?.storeRoot,
|
|
28862
|
+
defaultHistoryLimit: pluginConfig.chat?.defaultHistoryLimit,
|
|
28863
|
+
minReconnectMs: pluginConfig.chat?.minReconnectMs,
|
|
28864
|
+
maxReconnectMs: pluginConfig.chat?.maxReconnectMs,
|
|
28865
|
+
heartbeatIntervalMs: pluginConfig.chat?.heartbeatIntervalMs
|
|
28866
|
+
},
|
|
28867
|
+
handlers: {
|
|
28868
|
+
logger,
|
|
28869
|
+
onChatDeliver: async (frame) => {
|
|
28870
|
+
const walletAddress = parseWalletFromIdentity(frame.from);
|
|
28871
|
+
const marker = `[PERKOS_CHAT:${frame.convId}]`;
|
|
28872
|
+
const eventText = [
|
|
28873
|
+
marker,
|
|
28874
|
+
`From: ${frame.from}`,
|
|
28875
|
+
`Conversation: ${frame.convId}`,
|
|
28876
|
+
walletAddress ? `Wallet: ${walletAddress}` : "",
|
|
28877
|
+
"",
|
|
28878
|
+
"A user is messaging you in a PerkOS chat conversation.",
|
|
28879
|
+
"To respond, call the `perkos_chat_reply` tool with:",
|
|
28880
|
+
` convId: "${frame.convId}"`,
|
|
28881
|
+
walletAddress ? ` walletAddress: "${walletAddress}"` : ` walletAddress: <derived from From>`,
|
|
28882
|
+
` text: <your reply>`,
|
|
28883
|
+
"",
|
|
28884
|
+
"Message body:",
|
|
28885
|
+
frame.text
|
|
28886
|
+
].filter(Boolean).join("\n");
|
|
28887
|
+
const runtimeKind = pluginConfig.runtime?.kind || "openclaw";
|
|
28888
|
+
if (runtimeKind === "hermes" || runtimeKind === "hermes-api") {
|
|
28889
|
+
try {
|
|
28890
|
+
await deliverToHermes(pluginConfig, eventText, logger);
|
|
28891
|
+
} catch (err) {
|
|
28892
|
+
logger.error(`[perkos-chat] hermes delivery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
28893
|
+
}
|
|
28894
|
+
return;
|
|
28895
|
+
}
|
|
28896
|
+
if (runtimeKind === "none") {
|
|
28897
|
+
logger.info(`[perkos-chat] dropped chat_deliver for ${frame.convId} (runtime=none)`);
|
|
28898
|
+
return;
|
|
28899
|
+
}
|
|
28900
|
+
if (enqueueSystemEvent) {
|
|
28901
|
+
enqueueSystemEvent(eventText, { sessionKey: openclawEventSessionKey(pluginConfig) });
|
|
28902
|
+
}
|
|
28903
|
+
wakeGatewayAgent(
|
|
28904
|
+
requestHeartbeatNow,
|
|
28905
|
+
`[PerkOS-Chat] inbound message in ${frame.convId}`,
|
|
28906
|
+
logger,
|
|
28907
|
+
openclawWakeSessionKey(pluginConfig)
|
|
28908
|
+
);
|
|
28909
|
+
},
|
|
28910
|
+
onChannelJoin: async (frame) => {
|
|
28911
|
+
logger.info(`[perkos-chat] joined conv ${frame.convId} (host: ${frame.historyHost}, ${frame.participants.length} participants)`);
|
|
28912
|
+
}
|
|
28913
|
+
}
|
|
28914
|
+
}) : null;
|
|
28915
|
+
if (pluginConfig.chat?.enabled && !chatApiKey) {
|
|
28916
|
+
logger.error("[perkos-chat] chat.enabled=true but no apiKey (set chat.apiKey or fall back from relay.apiKey)");
|
|
28917
|
+
}
|
|
28918
|
+
if (chatClient) {
|
|
28919
|
+
api.registerService({
|
|
28920
|
+
id: "perkos-a2a-chat",
|
|
28921
|
+
start: () => {
|
|
28922
|
+
chatClient.start();
|
|
28923
|
+
logger.info(`[perkos-chat] client started for ${pluginConfig.agentName}`);
|
|
28924
|
+
},
|
|
28925
|
+
stop: () => {
|
|
28926
|
+
chatClient.stop();
|
|
28927
|
+
logger.info("[perkos-chat] client stopped");
|
|
28928
|
+
}
|
|
28929
|
+
});
|
|
28930
|
+
}
|
|
28044
28931
|
api.registerTool({
|
|
28045
28932
|
name: "perkos_a2a_send",
|
|
28046
28933
|
description: "Send a task to another agent via PerkOS A2A. Use this to delegate work, ask a peer to research, or route a chain/circular workflow. The message may name additional agents and a final notification target; the bridge preserves the route and prevents loops.",
|
|
@@ -28201,6 +29088,102 @@ function register(api) {
|
|
|
28201
29088
|
}
|
|
28202
29089
|
}
|
|
28203
29090
|
});
|
|
29091
|
+
api.registerTool({
|
|
29092
|
+
name: "perkos_chat_reply",
|
|
29093
|
+
description: "Send a reply into a PerkOS chat conversation. Use this when a user (or another agent) sent a chat message you should respond to. The system event that woke you contains `Conversation: <convId>` and `Wallet: <walletAddress>` lines you can copy directly. The reply is appended to local history first, then sent on the wire \u2014 so it's preserved even if the chat server is briefly unreachable.",
|
|
29094
|
+
parameters: {
|
|
29095
|
+
type: "object",
|
|
29096
|
+
properties: {
|
|
29097
|
+
convId: {
|
|
29098
|
+
type: "string",
|
|
29099
|
+
description: "Conversation id (UUID) \u2014 found in the [PERKOS_CHAT:<convId>] marker."
|
|
29100
|
+
},
|
|
29101
|
+
walletAddress: {
|
|
29102
|
+
type: "string",
|
|
29103
|
+
description: "Wallet that owns the conversation. From the `Wallet:` line of the system event."
|
|
29104
|
+
},
|
|
29105
|
+
text: {
|
|
29106
|
+
type: "string",
|
|
29107
|
+
description: "Reply body. Markdown supported."
|
|
29108
|
+
},
|
|
29109
|
+
replyTo: {
|
|
29110
|
+
type: "string",
|
|
29111
|
+
description: "Optional id of the inbound message you are replying to."
|
|
29112
|
+
}
|
|
29113
|
+
},
|
|
29114
|
+
required: ["convId", "walletAddress", "text"]
|
|
29115
|
+
},
|
|
29116
|
+
async execute(_id, params) {
|
|
29117
|
+
if (!chatClient) {
|
|
29118
|
+
return {
|
|
29119
|
+
content: [{ type: "text", text: "PerkOS-Chat client is not enabled in this agent's config." }]
|
|
29120
|
+
};
|
|
29121
|
+
}
|
|
29122
|
+
try {
|
|
29123
|
+
const result = await chatClient.sendReply({
|
|
29124
|
+
convId: params.convId,
|
|
29125
|
+
walletAddress: params.walletAddress,
|
|
29126
|
+
text: params.text,
|
|
29127
|
+
replyTo: params.replyTo ?? null
|
|
29128
|
+
});
|
|
29129
|
+
return {
|
|
29130
|
+
content: [{
|
|
29131
|
+
type: "text",
|
|
29132
|
+
text: [
|
|
29133
|
+
`Reply ${result.delivered ? "delivered" : "queued (offline)"}.`,
|
|
29134
|
+
`Message id: ${result.id}`,
|
|
29135
|
+
`Conv: ${params.convId}`
|
|
29136
|
+
].join("\n")
|
|
29137
|
+
}]
|
|
29138
|
+
};
|
|
29139
|
+
} catch (err) {
|
|
29140
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
29141
|
+
return { content: [{ type: "text", text: `Error sending chat reply: ${msg}` }] };
|
|
29142
|
+
}
|
|
29143
|
+
}
|
|
29144
|
+
});
|
|
29145
|
+
api.registerTool({
|
|
29146
|
+
name: "perkos_chat_history",
|
|
29147
|
+
description: "Read recent messages from a PerkOS chat conversation (this agent's local store). Returns up to `limit` messages older than `before` (ISO timestamp), most-recent-first ordered by timestamp. Use this to recall context before composing a reply.",
|
|
29148
|
+
parameters: {
|
|
29149
|
+
type: "object",
|
|
29150
|
+
properties: {
|
|
29151
|
+
convId: { type: "string", description: "Conversation id (UUID)." },
|
|
29152
|
+
before: {
|
|
29153
|
+
type: "string",
|
|
29154
|
+
description: "Optional ISO timestamp; only messages strictly older than this are returned. Omit to get the newest page."
|
|
29155
|
+
},
|
|
29156
|
+
limit: {
|
|
29157
|
+
type: "number",
|
|
29158
|
+
description: "Max messages to return (1-500, default 50)."
|
|
29159
|
+
}
|
|
29160
|
+
},
|
|
29161
|
+
required: ["convId"]
|
|
29162
|
+
},
|
|
29163
|
+
async execute(_id, params) {
|
|
29164
|
+
if (!chatClient) {
|
|
29165
|
+
return {
|
|
29166
|
+
content: [{ type: "text", text: "PerkOS-Chat client is not enabled in this agent's config." }]
|
|
29167
|
+
};
|
|
29168
|
+
}
|
|
29169
|
+
try {
|
|
29170
|
+
const store = chatClient.getStore();
|
|
29171
|
+
const page = await store.readPage(params.convId, {
|
|
29172
|
+
before: params.before ?? null,
|
|
29173
|
+
limit: params.limit ?? 50
|
|
29174
|
+
});
|
|
29175
|
+
if (page.messages.length === 0) {
|
|
29176
|
+
return { content: [{ type: "text", text: `No messages found in ${params.convId}.` }] };
|
|
29177
|
+
}
|
|
29178
|
+
const lines = page.messages.map((m) => `[${m.timestamp}] ${m.from}: ${m.text}`);
|
|
29179
|
+
if (page.hasMore) lines.push("", "(older messages available \u2014 call again with `before` set to the earliest timestamp)");
|
|
29180
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
29181
|
+
} catch (err) {
|
|
29182
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
29183
|
+
return { content: [{ type: "text", text: `Error reading chat history: ${msg}` }] };
|
|
29184
|
+
}
|
|
29185
|
+
}
|
|
29186
|
+
});
|
|
28204
29187
|
api.registerGatewayMethod("perkos-a2a.status", ({ respond }) => {
|
|
28205
29188
|
respond(true, {
|
|
28206
29189
|
agent: pluginConfig.agentName,
|
|
@@ -28213,7 +29196,12 @@ function register(api) {
|
|
|
28213
29196
|
pendingTasks: pendingTasks.length,
|
|
28214
29197
|
hasSystemEventInjection: !!enqueueSystemEvent,
|
|
28215
29198
|
protocol: "a2a",
|
|
28216
|
-
version: "0.8.4"
|
|
29199
|
+
version: "0.8.4",
|
|
29200
|
+
chat: chatClient ? {
|
|
29201
|
+
enabled: true,
|
|
29202
|
+
connected: chatClient.isConnected(),
|
|
29203
|
+
url: pluginConfig.chat?.url || "wss://chat.perkos.xyz/chat"
|
|
29204
|
+
} : { enabled: false, connected: false, url: null }
|
|
28217
29205
|
});
|
|
28218
29206
|
});
|
|
28219
29207
|
api.registerCli(
|
|
@@ -28229,6 +29217,11 @@ function register(api) {
|
|
|
28229
29217
|
console.log(`Auth required: ${pluginConfig.auth?.requireApiKey || false}`);
|
|
28230
29218
|
console.log(`Peers: ${Object.keys(pluginConfig.peers).join(", ") || "(none)"}`);
|
|
28231
29219
|
console.log(`System event injection: ${!!enqueueSystemEvent}`);
|
|
29220
|
+
if (chatClient) {
|
|
29221
|
+
console.log(`Chat: enabled (connected=${chatClient.isConnected()}, url=${pluginConfig.chat?.url || "wss://chat.perkos.xyz/chat"})`);
|
|
29222
|
+
} else {
|
|
29223
|
+
console.log("Chat: disabled");
|
|
29224
|
+
}
|
|
28232
29225
|
});
|
|
28233
29226
|
cmd.command("discover").description("Discover peer agents").action(async () => {
|
|
28234
29227
|
const peers = await server.discoverPeers();
|
|
@@ -28312,10 +29305,13 @@ Relay: ${pluginConfig.relay?.enabled ? "enabled" : "disabled"}`);
|
|
|
28312
29305
|
}
|
|
28313
29306
|
export {
|
|
28314
29307
|
A2AServer,
|
|
29308
|
+
ChatClient,
|
|
29309
|
+
ChatStore,
|
|
28315
29310
|
RelayClient,
|
|
28316
29311
|
RelayHub,
|
|
28317
29312
|
register as default,
|
|
28318
|
-
detectNetworking
|
|
29313
|
+
detectNetworking,
|
|
29314
|
+
isWebhookEvent
|
|
28319
29315
|
};
|
|
28320
29316
|
/*! Bundled license information:
|
|
28321
29317
|
|