@securecoms/agent-bridge 0.7.0 → 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 +2 -0
- package/connectors/hermes/__init__.py +60 -0
- package/connectors/hermes/adapter.py +470 -0
- package/connectors/hermes/plugin.yaml +25 -0
- package/connectors/hermes/pytest.ini +3 -0
- package/connectors/hermes/securecoms_gate.py +70 -0
- package/connectors/hermes/securecoms_transport.py +257 -0
- package/connectors/hermes/tests/conftest.py +23 -0
- package/connectors/hermes/tests/test_adapter_wiring.py +541 -0
- package/connectors/hermes/tests/test_securecoms_gate.py +113 -0
- package/connectors/hermes/tests/test_securecoms_transport.py +608 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +467 -67
- package/dist/bridge-agent.d.ts +4 -0
- package/dist/config.d.ts +4 -0
- package/dist/http-server.d.ts +4 -4
- package/dist/index.js +160 -18
- package/dist/memory/openclaw-provider.d.ts +10 -1
- package/dist/memory/openclaw-workspace-facts.d.ts +38 -0
- package/dist/memory/provider.d.ts +2 -1
- package/dist/service/harness-wire.d.ts +3 -2
- package/dist/service/hermes-config.d.ts +51 -0
- package/dist/service/wire.d.ts +97 -40
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -27,6 +27,8 @@ function loadConfig(opts) {
|
|
|
27
27
|
let memoryProvider;
|
|
28
28
|
let memoryBaseDir;
|
|
29
29
|
let memoryAgent;
|
|
30
|
+
let memoryWorkspaceDir;
|
|
31
|
+
let memoryFactsCharBudget;
|
|
30
32
|
if (memProvRaw === "none") {
|
|
31
33
|
memoryProvider = "none";
|
|
32
34
|
} else if (memProvRaw === "hermes") {
|
|
@@ -40,6 +42,10 @@ function loadConfig(opts) {
|
|
|
40
42
|
memoryBaseDir = typeof bd === "string" && bd ? bd : "~/.openclaw";
|
|
41
43
|
const ag = oc?.agent;
|
|
42
44
|
memoryAgent = typeof ag === "string" && ag ? ag : void 0;
|
|
45
|
+
const wd = oc?.workspace_dir;
|
|
46
|
+
memoryWorkspaceDir = typeof wd === "string" && wd ? wd : void 0;
|
|
47
|
+
const fb = oc?.facts_char_budget;
|
|
48
|
+
memoryFactsCharBudget = typeof fb === "number" && Number.isInteger(fb) && fb >= 0 ? fb : void 0;
|
|
43
49
|
} else {
|
|
44
50
|
memoryProvider = "securecoms";
|
|
45
51
|
}
|
|
@@ -56,7 +62,9 @@ function loadConfig(opts) {
|
|
|
56
62
|
rateLimitRefillPerMinute,
|
|
57
63
|
memoryProvider,
|
|
58
64
|
memoryBaseDir,
|
|
59
|
-
memoryAgent
|
|
65
|
+
memoryAgent,
|
|
66
|
+
memoryWorkspaceDir,
|
|
67
|
+
memoryFactsCharBudget
|
|
60
68
|
};
|
|
61
69
|
}
|
|
62
70
|
function arg(argv, flag) {
|
|
@@ -3947,7 +3955,7 @@ function loadPackageMeta(importMetaUrl) {
|
|
|
3947
3955
|
|
|
3948
3956
|
// src/bridge-agent.ts
|
|
3949
3957
|
import { promises as fsp } from "node:fs";
|
|
3950
|
-
import { join as
|
|
3958
|
+
import { join as join8, dirname as dirname3 } from "node:path";
|
|
3951
3959
|
|
|
3952
3960
|
// src/latency.ts
|
|
3953
3961
|
var DEFAULT_CAPACITY = 1024;
|
|
@@ -4460,29 +4468,153 @@ function readRecentTurns(baseDir, agent, sessionId, k) {
|
|
|
4460
4468
|
return k > 0 ? turns.slice(-k) : [];
|
|
4461
4469
|
}
|
|
4462
4470
|
|
|
4471
|
+
// src/memory/openclaw-workspace-facts.ts
|
|
4472
|
+
import { readdirSync, readFileSync as readFileSync5, statSync as statSync2 } from "node:fs";
|
|
4473
|
+
import { join as join7 } from "node:path";
|
|
4474
|
+
var DEFAULT_FACTS_BUDGET = 8e3;
|
|
4475
|
+
var MEMORY_MD_CAP = 6e3;
|
|
4476
|
+
var MIN_FRAGMENT = 200;
|
|
4477
|
+
var MAX_SOURCE_BYTES = 1024 * 1024;
|
|
4478
|
+
var DAILY_RE = /^\d{4}-\d{2}-\d{2}\.md$/;
|
|
4479
|
+
function resolveWorkspaceDir(baseDir, agent, override) {
|
|
4480
|
+
if (override) return expandHome(override);
|
|
4481
|
+
const base = expandHome(baseDir);
|
|
4482
|
+
const isMain2 = agent === "" || agent === "main";
|
|
4483
|
+
try {
|
|
4484
|
+
const cfg = JSON.parse(readFileSync5(join7(base, "openclaw.json"), "utf8"));
|
|
4485
|
+
const agents = cfg?.agents;
|
|
4486
|
+
if (agents && typeof agents === "object") {
|
|
4487
|
+
const list = Array.isArray(agents.list) ? agents.list : [];
|
|
4488
|
+
const wanted = isMain2 ? "main" : agent;
|
|
4489
|
+
const entry = list.find((e) => e && typeof e === "object" && e.id === wanted);
|
|
4490
|
+
const ws = entry?.workspace;
|
|
4491
|
+
if (typeof ws === "string" && ws) return expandHome(ws);
|
|
4492
|
+
const def = agents.defaults?.workspace;
|
|
4493
|
+
if (typeof def === "string" && def) return expandHome(def);
|
|
4494
|
+
}
|
|
4495
|
+
} catch {
|
|
4496
|
+
}
|
|
4497
|
+
return isMain2 ? join7(base, "workspace") : join7(base, `workspace-${agent}`);
|
|
4498
|
+
}
|
|
4499
|
+
var TRUNC = "\u2026[truncated]";
|
|
4500
|
+
function headSlice(s, n) {
|
|
4501
|
+
if (s.length <= n) return { text: s, clipped: false };
|
|
4502
|
+
let out = s.slice(0, n);
|
|
4503
|
+
const last = out.charCodeAt(out.length - 1);
|
|
4504
|
+
if (last >= 55296 && last <= 56319) out = out.slice(0, -1);
|
|
4505
|
+
return { text: out, clipped: true };
|
|
4506
|
+
}
|
|
4507
|
+
function tailSlice(s, n) {
|
|
4508
|
+
if (s.length <= n) return { text: s, clipped: false };
|
|
4509
|
+
let out = s.slice(s.length - n);
|
|
4510
|
+
const first = out.charCodeAt(0);
|
|
4511
|
+
if (first >= 56320 && first <= 57343) out = out.slice(1);
|
|
4512
|
+
return { text: out, clipped: true };
|
|
4513
|
+
}
|
|
4514
|
+
function readBounded(path) {
|
|
4515
|
+
if (statSync2(path).size > MAX_SOURCE_BYTES) return { text: null, oversize: true };
|
|
4516
|
+
return { text: readFileSync5(path, "utf8"), oversize: false };
|
|
4517
|
+
}
|
|
4518
|
+
function readWorkspaceFacts(baseDir, agent, opts) {
|
|
4519
|
+
const raw = opts?.charBudget;
|
|
4520
|
+
const budget = typeof raw === "number" && Number.isInteger(raw) && raw >= 0 ? raw : DEFAULT_FACTS_BUDGET;
|
|
4521
|
+
if (budget === 0) return { facts: [], truncated: false, missing: false };
|
|
4522
|
+
const dir = resolveWorkspaceDir(baseDir, agent, opts?.workspaceDir);
|
|
4523
|
+
const facts = [];
|
|
4524
|
+
let truncated = false;
|
|
4525
|
+
let remaining = budget;
|
|
4526
|
+
try {
|
|
4527
|
+
const r = readBounded(join7(dir, "MEMORY.md"));
|
|
4528
|
+
if (r.oversize) truncated = true;
|
|
4529
|
+
else if (r.text && r.text.trim()) {
|
|
4530
|
+
const { text, clipped } = headSlice(r.text, Math.min(MEMORY_MD_CAP, budget));
|
|
4531
|
+
truncated ||= clipped;
|
|
4532
|
+
facts.push(`workspace MEMORY.md:
|
|
4533
|
+
${text}${clipped ? `
|
|
4534
|
+
${TRUNC}` : ""}`);
|
|
4535
|
+
remaining -= text.length;
|
|
4536
|
+
}
|
|
4537
|
+
} catch {
|
|
4538
|
+
}
|
|
4539
|
+
try {
|
|
4540
|
+
const dailies = readdirSync(join7(dir, "memory")).filter((f) => DAILY_RE.test(f)).sort().reverse().slice(0, 2);
|
|
4541
|
+
for (const f of dailies) {
|
|
4542
|
+
if (remaining < MIN_FRAGMENT) {
|
|
4543
|
+
truncated = true;
|
|
4544
|
+
break;
|
|
4545
|
+
}
|
|
4546
|
+
let r;
|
|
4547
|
+
try {
|
|
4548
|
+
r = readBounded(join7(dir, "memory", f));
|
|
4549
|
+
} catch {
|
|
4550
|
+
continue;
|
|
4551
|
+
}
|
|
4552
|
+
if (r.oversize) {
|
|
4553
|
+
truncated = true;
|
|
4554
|
+
continue;
|
|
4555
|
+
}
|
|
4556
|
+
if (!r.text || !r.text.trim()) continue;
|
|
4557
|
+
const { text, clipped } = tailSlice(r.text, remaining);
|
|
4558
|
+
truncated ||= clipped;
|
|
4559
|
+
facts.push(`workspace memory/${f} (daily):
|
|
4560
|
+
${clipped ? `${TRUNC}
|
|
4561
|
+
` : ""}${text}`);
|
|
4562
|
+
remaining -= text.length;
|
|
4563
|
+
}
|
|
4564
|
+
} catch {
|
|
4565
|
+
}
|
|
4566
|
+
return { facts, truncated, missing: facts.length === 0 };
|
|
4567
|
+
}
|
|
4568
|
+
|
|
4463
4569
|
// src/memory/openclaw-provider.ts
|
|
4464
4570
|
var DEFAULT_K2 = 200;
|
|
4465
4571
|
var OpenClawMemoryProvider = class {
|
|
4466
|
-
|
|
4572
|
+
// gauge: last successful returned total, including prefixes/markers
|
|
4573
|
+
constructor(store, baseDir, agent, factsOpts) {
|
|
4467
4574
|
this.store = store;
|
|
4468
4575
|
this.baseDir = baseDir;
|
|
4469
4576
|
this.agent = agent;
|
|
4577
|
+
this.factsOpts = factsOpts;
|
|
4470
4578
|
}
|
|
4471
4579
|
noSession = 0;
|
|
4472
4580
|
readFailed = 0;
|
|
4581
|
+
factsMissing = 0;
|
|
4582
|
+
// absence-shaped: resolved path missing or zero sources (per-call)
|
|
4583
|
+
factsReadFailed = 0;
|
|
4584
|
+
// anything THROWN out of the facts computation
|
|
4585
|
+
factsTruncated = 0;
|
|
4586
|
+
// budget clipped/dropped a source (incl. the 1MB skip)
|
|
4587
|
+
factsChars = 0;
|
|
4473
4588
|
async record(input) {
|
|
4474
4589
|
if (input.modality !== "voice") return { seq: 0, deduped: true };
|
|
4475
4590
|
return this.store.recordMemory({ ...input, text: `[voice] ${input.text}` });
|
|
4476
4591
|
}
|
|
4477
4592
|
async context(conversationId, limit) {
|
|
4478
4593
|
const k = limit ?? DEFAULT_K2;
|
|
4594
|
+
let wsFacts = [];
|
|
4595
|
+
try {
|
|
4596
|
+
const r = readWorkspaceFacts(this.baseDir, this.agent, {
|
|
4597
|
+
workspaceDir: this.factsOpts?.workspaceDir,
|
|
4598
|
+
charBudget: this.factsOpts?.factsCharBudget
|
|
4599
|
+
});
|
|
4600
|
+
wsFacts = r.facts;
|
|
4601
|
+
if (r.missing) this.factsMissing++;
|
|
4602
|
+
if (r.truncated) this.factsTruncated++;
|
|
4603
|
+
this.factsChars = r.facts.reduce((n, f) => n + f.length, 0);
|
|
4604
|
+
} catch {
|
|
4605
|
+
this.factsReadFailed++;
|
|
4606
|
+
}
|
|
4607
|
+
const withFacts = (res) => ({
|
|
4608
|
+
recent: res.recent,
|
|
4609
|
+
facts: [...wsFacts, ...res.facts]
|
|
4610
|
+
});
|
|
4479
4611
|
const voice = await this.store.memoryContext(conversationId, k);
|
|
4480
4612
|
let textTurns = [];
|
|
4481
4613
|
try {
|
|
4482
4614
|
const sid = resolveOwnerDmSession(this.baseDir, this.agent);
|
|
4483
4615
|
if (!sid) {
|
|
4484
4616
|
this.noSession++;
|
|
4485
|
-
return voice;
|
|
4617
|
+
return withFacts(voice);
|
|
4486
4618
|
}
|
|
4487
4619
|
textTurns = readRecentTurns(this.baseDir, this.agent, sid, k).map((t) => ({
|
|
4488
4620
|
seq: 0,
|
|
@@ -4492,7 +4624,7 @@ var OpenClawMemoryProvider = class {
|
|
|
4492
4624
|
}));
|
|
4493
4625
|
} catch {
|
|
4494
4626
|
this.readFailed++;
|
|
4495
|
-
return voice;
|
|
4627
|
+
return withFacts(voice);
|
|
4496
4628
|
}
|
|
4497
4629
|
const tagged = [
|
|
4498
4630
|
...textTurns.map((t, i) => ({ ...t, _src: 0, _i: i })),
|
|
@@ -4511,10 +4643,17 @@ var OpenClawMemoryProvider = class {
|
|
|
4511
4643
|
text: t.text,
|
|
4512
4644
|
ts: t.ts
|
|
4513
4645
|
}));
|
|
4514
|
-
return { recent, facts: voice.facts };
|
|
4646
|
+
return withFacts({ recent, facts: voice.facts });
|
|
4515
4647
|
}
|
|
4516
4648
|
stats() {
|
|
4517
|
-
return {
|
|
4649
|
+
return {
|
|
4650
|
+
openclaw_no_session: this.noSession,
|
|
4651
|
+
openclaw_read_failed: this.readFailed,
|
|
4652
|
+
openclaw_facts_missing: this.factsMissing,
|
|
4653
|
+
openclaw_facts_read_failed: this.factsReadFailed,
|
|
4654
|
+
openclaw_facts_truncated: this.factsTruncated,
|
|
4655
|
+
openclaw_facts_chars: this.factsChars
|
|
4656
|
+
};
|
|
4518
4657
|
}
|
|
4519
4658
|
};
|
|
4520
4659
|
|
|
@@ -4538,10 +4677,10 @@ var NullMemory = class {
|
|
|
4538
4677
|
return { recent: [], facts: [] };
|
|
4539
4678
|
}
|
|
4540
4679
|
};
|
|
4541
|
-
function createMemoryProvider(provider, store, baseDir, agent) {
|
|
4680
|
+
function createMemoryProvider(provider, store, baseDir, agent, openclawFacts) {
|
|
4542
4681
|
if (provider === "none") return new NullMemory();
|
|
4543
4682
|
if (provider === "hermes") return new HermesMemoryProvider(baseDir ?? "~/.hermes");
|
|
4544
|
-
if (provider === "openclaw") return new OpenClawMemoryProvider(store, baseDir ?? "~/.openclaw", agent ?? "");
|
|
4683
|
+
if (provider === "openclaw") return new OpenClawMemoryProvider(store, baseDir ?? "~/.openclaw", agent ?? "", openclawFacts);
|
|
4545
4684
|
return new SecureComsLocalMemory(store);
|
|
4546
4685
|
}
|
|
4547
4686
|
|
|
@@ -4649,7 +4788,10 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
4649
4788
|
identityId: cfg.identityId,
|
|
4650
4789
|
cipherFor: () => new KeyedInboxCipher(deriveMlsStoreKey(seed))
|
|
4651
4790
|
});
|
|
4652
|
-
this.memory = createMemoryProvider(this.cfg.memoryProvider, this.store, this.cfg.memoryBaseDir, this.cfg.memoryAgent
|
|
4791
|
+
this.memory = createMemoryProvider(this.cfg.memoryProvider, this.store, this.cfg.memoryBaseDir, this.cfg.memoryAgent, {
|
|
4792
|
+
workspaceDir: this.cfg.memoryWorkspaceDir,
|
|
4793
|
+
factsCharBudget: this.cfg.memoryFactsCharBudget
|
|
4794
|
+
});
|
|
4653
4795
|
this.provider = new MlsGroupKeyProvider({
|
|
4654
4796
|
store: this.store,
|
|
4655
4797
|
backend: this.backend,
|
|
@@ -4729,7 +4871,7 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
4729
4871
|
const j = await res.json();
|
|
4730
4872
|
return String(j.attachment_id);
|
|
4731
4873
|
};
|
|
4732
|
-
this.attachmentsDirImpl = deps.attachmentsDir ??
|
|
4874
|
+
this.attachmentsDirImpl = deps.attachmentsDir ?? join8(dirname3(cfg.storePath), "attachments");
|
|
4733
4875
|
this.action = {
|
|
4734
4876
|
begin: (channelId) => this.actionBegin(channelId),
|
|
4735
4877
|
start: (opts) => this.actionStart(opts),
|
|
@@ -6257,11 +6399,11 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
6257
6399
|
|
|
6258
6400
|
// src/auth.ts
|
|
6259
6401
|
import { randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
|
|
6260
|
-
import { existsSync, readFileSync as
|
|
6402
|
+
import { existsSync, readFileSync as readFileSync6, writeFileSync } from "node:fs";
|
|
6261
6403
|
function resolveToken(opts) {
|
|
6262
6404
|
if (opts.token) return opts.token;
|
|
6263
6405
|
const f = opts.tokenFile;
|
|
6264
|
-
if (f && existsSync(f)) return
|
|
6406
|
+
if (f && existsSync(f)) return readFileSync6(f, "utf8").trim();
|
|
6265
6407
|
const tok = randomBytes3(32).toString("base64url");
|
|
6266
6408
|
if (f) writeFileSync(f, tok, { mode: 384 });
|
|
6267
6409
|
return tok;
|
|
@@ -6832,8 +6974,8 @@ async function handleInbound(sock, agent, data, inflight) {
|
|
|
6832
6974
|
}
|
|
6833
6975
|
|
|
6834
6976
|
// src/bin.ts
|
|
6835
|
-
import { readFileSync as
|
|
6836
|
-
import { join as
|
|
6977
|
+
import { readFileSync as readFileSync8, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3, rmSync, existsSync as existsSync2, realpathSync, readdirSync as readdirSync2, statSync as statSync3 } from "node:fs";
|
|
6978
|
+
import { join as join12, dirname as dirname7, isAbsolute as isAbsolute2, resolve as resolve2 } from "node:path";
|
|
6837
6979
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
6838
6980
|
import { homedir as homedir2, userInfo } from "node:os";
|
|
6839
6981
|
import { spawnSync } from "node:child_process";
|
|
@@ -7042,11 +7184,164 @@ function uninstallService(input, deps) {
|
|
|
7042
7184
|
}
|
|
7043
7185
|
|
|
7044
7186
|
// src/service/install-prep.ts
|
|
7045
|
-
import { join as
|
|
7187
|
+
import { join as join10 } from "node:path";
|
|
7046
7188
|
|
|
7047
7189
|
// src/service/wire.ts
|
|
7048
|
-
import { join as
|
|
7190
|
+
import { join as join9, dirname as dirname5 } from "node:path";
|
|
7049
7191
|
import { fileURLToPath } from "node:url";
|
|
7192
|
+
|
|
7193
|
+
// src/service/hermes-config.ts
|
|
7194
|
+
var HERMES_PLUGIN_NAME = "securecoms-platform";
|
|
7195
|
+
function indentOf(line) {
|
|
7196
|
+
const m = line.match(/^( *)/);
|
|
7197
|
+
return m ? m[1].length : 0;
|
|
7198
|
+
}
|
|
7199
|
+
function isCommentLine(line) {
|
|
7200
|
+
return /^\s*#/.test(line);
|
|
7201
|
+
}
|
|
7202
|
+
function isBlank(line) {
|
|
7203
|
+
return line.trim() === "";
|
|
7204
|
+
}
|
|
7205
|
+
function parseKeyLine(line, key) {
|
|
7206
|
+
const m = line.match(new RegExp(`^( *)${key}:(.*)$`));
|
|
7207
|
+
if (!m) return null;
|
|
7208
|
+
const indent = m[1].length;
|
|
7209
|
+
const rest = m[2].trim();
|
|
7210
|
+
if (rest === "" || rest.startsWith("#")) return { idx: -1, indent, inline: null };
|
|
7211
|
+
return { idx: -1, indent, inline: rest };
|
|
7212
|
+
}
|
|
7213
|
+
function findKeyLine(lines, key, start, end) {
|
|
7214
|
+
for (let i = start; i < end; i++) {
|
|
7215
|
+
const parsed = parseKeyLine(lines[i], key);
|
|
7216
|
+
if (parsed) return { ...parsed, idx: i };
|
|
7217
|
+
}
|
|
7218
|
+
return null;
|
|
7219
|
+
}
|
|
7220
|
+
function findBlockEnd(lines, start, parentIndent) {
|
|
7221
|
+
let i = start;
|
|
7222
|
+
for (; i < lines.length; i++) {
|
|
7223
|
+
if (isBlank(lines[i])) continue;
|
|
7224
|
+
if (indentOf(lines[i]) > parentIndent) continue;
|
|
7225
|
+
break;
|
|
7226
|
+
}
|
|
7227
|
+
return i;
|
|
7228
|
+
}
|
|
7229
|
+
function stripQuotesAndComment(raw) {
|
|
7230
|
+
let value = raw.replace(/\s+#.*$/, "").trim();
|
|
7231
|
+
if (value.length >= 2) {
|
|
7232
|
+
const first = value[0];
|
|
7233
|
+
const last = value[value.length - 1];
|
|
7234
|
+
if (first === '"' && last === '"' || first === "'" && last === "'") {
|
|
7235
|
+
value = value.slice(1, -1);
|
|
7236
|
+
}
|
|
7237
|
+
}
|
|
7238
|
+
return value;
|
|
7239
|
+
}
|
|
7240
|
+
function collectItems(lines, start) {
|
|
7241
|
+
const items = [];
|
|
7242
|
+
let i = start;
|
|
7243
|
+
for (; i < lines.length; i++) {
|
|
7244
|
+
const line = lines[i];
|
|
7245
|
+
if (isCommentLine(line)) {
|
|
7246
|
+
return { items, endIdx: i, ambiguous: true };
|
|
7247
|
+
}
|
|
7248
|
+
const m = line.match(/^( *)-\s*(.+?)\s*$/);
|
|
7249
|
+
if (!m) break;
|
|
7250
|
+
items.push({ idx: i, indent: m[1].length, value: stripQuotesAndComment(m[2]) });
|
|
7251
|
+
}
|
|
7252
|
+
return { items, endIdx: i, ambiguous: false };
|
|
7253
|
+
}
|
|
7254
|
+
var SIBLING_KEY_RE = /^[\w.-]+:(\s|$)/;
|
|
7255
|
+
function isLegitimateListEnd(lines, stopIdx, itemCol, enabledIndent) {
|
|
7256
|
+
if (stopIdx >= lines.length) return true;
|
|
7257
|
+
if (!isBlank(lines[stopIdx])) {
|
|
7258
|
+
return indentOf(lines[stopIdx]) === enabledIndent && SIBLING_KEY_RE.test(lines[stopIdx].trim());
|
|
7259
|
+
}
|
|
7260
|
+
let i = stopIdx;
|
|
7261
|
+
while (i < lines.length && isBlank(lines[i])) i++;
|
|
7262
|
+
if (i >= lines.length) return true;
|
|
7263
|
+
return indentOf(lines[i]) < itemCol;
|
|
7264
|
+
}
|
|
7265
|
+
function parseInlineList(inline) {
|
|
7266
|
+
const trimmed = inline.trim();
|
|
7267
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return [];
|
|
7268
|
+
const inner = trimmed.slice(1, -1).trim();
|
|
7269
|
+
if (inner === "") return [];
|
|
7270
|
+
return inner.split(",").map((s) => stripQuotesAndComment(s.trim()));
|
|
7271
|
+
}
|
|
7272
|
+
function noop(reason) {
|
|
7273
|
+
return reason ? { action: "noop", reason } : { action: "noop" };
|
|
7274
|
+
}
|
|
7275
|
+
var MANUAL_INSTRUCTIONS = [
|
|
7276
|
+
"add the following manually under the plugin's config (`~/.hermes/config.yaml`):",
|
|
7277
|
+
" plugins:",
|
|
7278
|
+
" enabled:",
|
|
7279
|
+
` - ${HERMES_PLUGIN_NAME}`
|
|
7280
|
+
].join("\n");
|
|
7281
|
+
function ensureHermesPluginEnabled(configText) {
|
|
7282
|
+
const eol = configText.includes("\r\n") ? "\r\n" : "\n";
|
|
7283
|
+
const lines = configText.split(/\r\n|\n/);
|
|
7284
|
+
const pluginsIdx = lines.findIndex((l) => /^plugins:\s*(#.*)?$/.test(l));
|
|
7285
|
+
if (pluginsIdx === -1) {
|
|
7286
|
+
return noop(`no \`plugins:\` block found in config.yaml \u2014 ${MANUAL_INSTRUCTIONS}`);
|
|
7287
|
+
}
|
|
7288
|
+
const pluginsIndent = indentOf(lines[pluginsIdx]);
|
|
7289
|
+
const blockEnd = findBlockEnd(lines, pluginsIdx + 1, pluginsIndent);
|
|
7290
|
+
const enabledKey = findKeyLine(lines, "enabled", pluginsIdx + 1, blockEnd);
|
|
7291
|
+
if (!enabledKey) {
|
|
7292
|
+
return noop(`no \`plugins.enabled\` key found in config.yaml \u2014 ${MANUAL_INSTRUCTIONS}`);
|
|
7293
|
+
}
|
|
7294
|
+
if (enabledKey.inline !== null) {
|
|
7295
|
+
return noop(
|
|
7296
|
+
`plugins.enabled uses the inline \`enabled: [...]\` form, which this editor refuses to rewrite (never reformat) \u2014 ${MANUAL_INSTRUCTIONS}`
|
|
7297
|
+
);
|
|
7298
|
+
}
|
|
7299
|
+
const { items, endIdx, ambiguous } = collectItems(lines, enabledKey.idx + 1);
|
|
7300
|
+
if (ambiguous) {
|
|
7301
|
+
return noop(
|
|
7302
|
+
`a comment line interrupts the plugins.enabled list \u2014 refusing rather than guess whether it belongs to the item above or below; ${MANUAL_INSTRUCTIONS}`
|
|
7303
|
+
);
|
|
7304
|
+
}
|
|
7305
|
+
if (items.length === 0) {
|
|
7306
|
+
return noop(`plugins.enabled has no items to copy an indent column from (empty list) \u2014 ${MANUAL_INSTRUCTIONS}`);
|
|
7307
|
+
}
|
|
7308
|
+
const col = items[0].indent;
|
|
7309
|
+
if (items.some((it) => it.indent !== col)) {
|
|
7310
|
+
return noop(`plugins.enabled items are inconsistently indented \u2014 refusing to guess the right column; ${MANUAL_INSTRUCTIONS}`);
|
|
7311
|
+
}
|
|
7312
|
+
const disabledKey = findKeyLine(lines, "disabled", pluginsIdx + 1, blockEnd);
|
|
7313
|
+
let disabledEntries = [];
|
|
7314
|
+
if (disabledKey) {
|
|
7315
|
+
if (disabledKey.inline !== null) {
|
|
7316
|
+
disabledEntries = parseInlineList(disabledKey.inline);
|
|
7317
|
+
} else {
|
|
7318
|
+
disabledEntries = collectItems(lines, disabledKey.idx + 1).items.map((it) => it.value);
|
|
7319
|
+
}
|
|
7320
|
+
}
|
|
7321
|
+
if (disabledEntries.includes(HERMES_PLUGIN_NAME)) {
|
|
7322
|
+
return noop(
|
|
7323
|
+
`${HERMES_PLUGIN_NAME} is listed under plugins.disabled, which wins over plugins.enabled in the Hermes loader \u2014 remove it from plugins.disabled (or move it to plugins.enabled) manually, then re-run wire.`
|
|
7324
|
+
);
|
|
7325
|
+
}
|
|
7326
|
+
if (items.some((it) => it.value === HERMES_PLUGIN_NAME)) {
|
|
7327
|
+
return { action: "noop" };
|
|
7328
|
+
}
|
|
7329
|
+
if (!isLegitimateListEnd(lines, endIdx, col, enabledKey.indent)) {
|
|
7330
|
+
return noop(
|
|
7331
|
+
`plugins.enabled's list doesn't end at a recognized boundary (expected a sibling key, a blank-then-dedent, or end of file) \u2014 refusing to insert at an unverified spot; ${MANUAL_INSTRUCTIONS}`
|
|
7332
|
+
);
|
|
7333
|
+
}
|
|
7334
|
+
const insertLine = `${" ".repeat(col)}- ${HERMES_PLUGIN_NAME}`;
|
|
7335
|
+
const newLines = [...lines.slice(0, endIdx), insertLine, ...lines.slice(endIdx)];
|
|
7336
|
+
return { action: "edited", text: newLines.join(eol) };
|
|
7337
|
+
}
|
|
7338
|
+
|
|
7339
|
+
// src/service/wire.ts
|
|
7340
|
+
var CONNECTOR_FILE_EXTENSIONS = {
|
|
7341
|
+
openclaw: [".js", ".json"],
|
|
7342
|
+
hermes: [".py", ".yaml", ".json"]
|
|
7343
|
+
};
|
|
7344
|
+
var CONNECTOR_SKIP_DIRS = /* @__PURE__ */ new Set(["tests", "__pycache__"]);
|
|
7050
7345
|
function detectHarness(home, statDir) {
|
|
7051
7346
|
if (statDir(`${home}/.openclaw`)) return "openclaw";
|
|
7052
7347
|
if (statDir(`${home}/.hermes`)) return "hermes";
|
|
@@ -7054,11 +7349,41 @@ function detectHarness(home, statDir) {
|
|
|
7054
7349
|
}
|
|
7055
7350
|
function connectorTargetDir(home, harness) {
|
|
7056
7351
|
if (harness === "openclaw") return `${home}/.openclaw/local-plugins/securecoms`;
|
|
7057
|
-
return `${home}/.hermes/
|
|
7352
|
+
return `${home}/.hermes/plugins/securecoms`;
|
|
7058
7353
|
}
|
|
7059
|
-
function connectorsSrcDir(importMetaUrl) {
|
|
7354
|
+
function connectorsSrcDir(importMetaUrl, harness) {
|
|
7060
7355
|
const pkgRoot = dirname5(dirname5(fileURLToPath(importMetaUrl)));
|
|
7061
|
-
return
|
|
7356
|
+
return join9(pkgRoot, "connectors", harness);
|
|
7357
|
+
}
|
|
7358
|
+
function hermesConfigPath(home) {
|
|
7359
|
+
return `${home}/.hermes/config.yaml`;
|
|
7360
|
+
}
|
|
7361
|
+
function hermesConfigBackupPath(configPath, ts, pid) {
|
|
7362
|
+
return `${configPath}.bak-wire-${ts}-${pid}`;
|
|
7363
|
+
}
|
|
7364
|
+
function enableHermesConfig(home, deps) {
|
|
7365
|
+
const path = hermesConfigPath(home);
|
|
7366
|
+
const text = deps.readHermesConfig(path);
|
|
7367
|
+
if (text === null) {
|
|
7368
|
+
return {
|
|
7369
|
+
ok: false,
|
|
7370
|
+
message: `Hermes config.yaml not found at ${path} (is ~/.hermes installed?) \u2014 add the plugin manually:
|
|
7371
|
+
plugins:
|
|
7372
|
+
enabled:
|
|
7373
|
+
- ${HERMES_PLUGIN_NAME}`
|
|
7374
|
+
};
|
|
7375
|
+
}
|
|
7376
|
+
const result = ensureHermesPluginEnabled(text);
|
|
7377
|
+
if (result.action === "noop") {
|
|
7378
|
+
if (result.reason) return { ok: false, message: result.reason };
|
|
7379
|
+
return { ok: true, message: `${HERMES_PLUGIN_NAME} is already enabled in ${path}.` };
|
|
7380
|
+
}
|
|
7381
|
+
deps.backupHermesConfig(path, text);
|
|
7382
|
+
deps.writeHermesConfig(path, result.text);
|
|
7383
|
+
return {
|
|
7384
|
+
ok: true,
|
|
7385
|
+
message: `enabled ${HERMES_PLUGIN_NAME} in ${path} (the original was backed up alongside it as config.yaml.bak-wire-<ts>-<pid>).`
|
|
7386
|
+
};
|
|
7062
7387
|
}
|
|
7063
7388
|
function parseArg(argv, flag) {
|
|
7064
7389
|
const i = argv.indexOf(flag);
|
|
@@ -7086,15 +7411,19 @@ function runWire(deps) {
|
|
|
7086
7411
|
const resolved = resolveEntry(deps);
|
|
7087
7412
|
if (!resolved) return 1;
|
|
7088
7413
|
const { entry, harness } = resolved;
|
|
7089
|
-
if (harness === "hermes") {
|
|
7090
|
-
deps.log(
|
|
7091
|
-
"[securecoms] Hermes connector is not available yet (fast-follow); detection and target dir only. Nothing installed."
|
|
7092
|
-
);
|
|
7093
|
-
return 1;
|
|
7094
|
-
}
|
|
7095
7414
|
const targetDir = connectorTargetDir(deps.home, harness);
|
|
7096
|
-
deps.copyConnector(deps.connectorsSrcDir, targetDir);
|
|
7415
|
+
deps.copyConnector(deps.connectorsSrcDir(harness), targetDir, harness);
|
|
7097
7416
|
deps.writeBinding(targetDir, entry.identityId);
|
|
7417
|
+
if (harness === "hermes") {
|
|
7418
|
+
const enabled = enableHermesConfig(deps.home, deps);
|
|
7419
|
+
deps.log(`[securecoms] ${enabled.message}`);
|
|
7420
|
+
if (!enabled.ok) {
|
|
7421
|
+
deps.log(
|
|
7422
|
+
"wire: Hermes connector files were copied and the binding was written, but the config.yaml plugin entry was NOT enabled automatically \u2014 the harness is NOT wired until you add it manually (see above) and restart the gateway."
|
|
7423
|
+
);
|
|
7424
|
+
return 1;
|
|
7425
|
+
}
|
|
7426
|
+
}
|
|
7098
7427
|
if (typeof entry.port === "number") {
|
|
7099
7428
|
const h = deps.getHealth(entry.port);
|
|
7100
7429
|
if (h && (h.agent_sessions ?? 0) >= 1) {
|
|
@@ -7105,17 +7434,23 @@ function runWire(deps) {
|
|
|
7105
7434
|
} else {
|
|
7106
7435
|
deps.log(`wire: connector installed (no bridge port known yet) \u2014 restart the harness to pick it up.`);
|
|
7107
7436
|
}
|
|
7437
|
+
if (harness === "hermes") {
|
|
7438
|
+
deps.log(
|
|
7439
|
+
"wire: restart the Hermes gateway (`launchctl kickstart -k gui/$UID/ai.hermes.gateway` or your service manager) to pick up the connector."
|
|
7440
|
+
);
|
|
7441
|
+
}
|
|
7108
7442
|
return 0;
|
|
7109
7443
|
}
|
|
7110
7444
|
function runUnwire(deps) {
|
|
7111
7445
|
const resolved = resolveEntry(deps);
|
|
7112
7446
|
if (!resolved) return 1;
|
|
7113
7447
|
const { harness } = resolved;
|
|
7448
|
+
deps.removeBinding(connectorTargetDir(deps.home, harness));
|
|
7114
7449
|
if (harness === "hermes") {
|
|
7115
|
-
deps.log(
|
|
7116
|
-
|
|
7450
|
+
deps.log(
|
|
7451
|
+
"[securecoms] Hermes binding removed. The connector files under ~/.hermes/plugins/securecoms and the plugins.enabled entry in config.yaml remain \u2014 removing a listed plugin's files would make Hermes log load errors at boot. Edit config.yaml manually to fully remove it, then restart the gateway."
|
|
7452
|
+
);
|
|
7117
7453
|
}
|
|
7118
|
-
deps.removeBinding(connectorTargetDir(deps.home, harness));
|
|
7119
7454
|
return 0;
|
|
7120
7455
|
}
|
|
7121
7456
|
|
|
@@ -7132,8 +7467,8 @@ function customFacts(input) {
|
|
|
7132
7467
|
function autoWireHarness(input, deps) {
|
|
7133
7468
|
const h = input.harness;
|
|
7134
7469
|
if (!h || h === "none") return { echo: "", wired: false, error: false };
|
|
7135
|
-
const retry = `agent-bridge wire --harness openclaw --identity ${input.slug}`;
|
|
7136
7470
|
if (h === "openclaw") {
|
|
7471
|
+
const retry = `agent-bridge wire --harness openclaw --identity ${input.slug}`;
|
|
7137
7472
|
const parent = `${input.home}/.openclaw`;
|
|
7138
7473
|
if (!deps.statDir(parent)) {
|
|
7139
7474
|
return {
|
|
@@ -7147,7 +7482,7 @@ function autoWireHarness(input, deps) {
|
|
|
7147
7482
|
const targetDir = connectorTargetDir(input.home, "openclaw");
|
|
7148
7483
|
try {
|
|
7149
7484
|
const prev = deps.readBinding(targetDir);
|
|
7150
|
-
deps.copyConnector(input.connectorsSrcDir, targetDir);
|
|
7485
|
+
deps.copyConnector(input.connectorsSrcDir, targetDir, "openclaw");
|
|
7151
7486
|
deps.writeBinding(targetDir, input.identityId);
|
|
7152
7487
|
const rebind = prev && prev !== input.identityId ? `
|
|
7153
7488
|
rebind: ${prev} \u2192 ${input.identityId}` : "";
|
|
@@ -7167,8 +7502,48 @@ function autoWireHarness(input, deps) {
|
|
|
7167
7502
|
}
|
|
7168
7503
|
}
|
|
7169
7504
|
if (h === "hermes") {
|
|
7170
|
-
|
|
7171
|
-
|
|
7505
|
+
const retry = `agent-bridge wire --harness hermes --identity ${input.slug}`;
|
|
7506
|
+
const parent = `${input.home}/.hermes`;
|
|
7507
|
+
if (!deps.statDir(parent)) {
|
|
7508
|
+
return {
|
|
7509
|
+
echo: `[securecoms] Hermes not found at ~/.hermes \u2014 the bridge service is installed, but the harness was NOT wired.
|
|
7510
|
+
Install Hermes, then re-run install, or run: ${retry}
|
|
7511
|
+
(or re-run install with --harness custom).`,
|
|
7512
|
+
wired: false,
|
|
7513
|
+
error: true
|
|
7514
|
+
};
|
|
7515
|
+
}
|
|
7516
|
+
const targetDir = connectorTargetDir(input.home, "hermes");
|
|
7517
|
+
try {
|
|
7518
|
+
const prev = deps.readBinding(targetDir);
|
|
7519
|
+
deps.copyConnector(input.connectorsSrcDir, targetDir, "hermes");
|
|
7520
|
+
deps.writeBinding(targetDir, input.identityId);
|
|
7521
|
+
const enabled = enableHermesConfig(input.home, deps);
|
|
7522
|
+
if (!enabled.ok) {
|
|
7523
|
+
return {
|
|
7524
|
+
echo: `[securecoms] Hermes connector copied and bound to identity ${input.identityId}, but the config.yaml plugin entry was NOT enabled automatically: ${enabled.message}
|
|
7525
|
+
The harness is NOT wired until you add it manually and restart the gateway.`,
|
|
7526
|
+
wired: false,
|
|
7527
|
+
error: true
|
|
7528
|
+
};
|
|
7529
|
+
}
|
|
7530
|
+
const rebind = prev && prev !== input.identityId ? `
|
|
7531
|
+
rebind: ${prev} \u2192 ${input.identityId}` : "";
|
|
7532
|
+
return {
|
|
7533
|
+
echo: `[securecoms] Hermes connector installed and bound to identity ${input.identityId}.${rebind}
|
|
7534
|
+
${enabled.message}
|
|
7535
|
+
Restart the Hermes gateway (\`launchctl kickstart -k gui/$UID/ai.hermes.gateway\` or your service manager) after install completes.`,
|
|
7536
|
+
wired: true,
|
|
7537
|
+
error: false
|
|
7538
|
+
};
|
|
7539
|
+
} catch (e) {
|
|
7540
|
+
return {
|
|
7541
|
+
echo: `[securecoms] the bridge service is installed, but wiring the Hermes connector FAILED: ${e.message}
|
|
7542
|
+
Retry after fixing the path: ${retry}`,
|
|
7543
|
+
wired: false,
|
|
7544
|
+
error: true
|
|
7545
|
+
};
|
|
7546
|
+
}
|
|
7172
7547
|
}
|
|
7173
7548
|
return { echo: `[securecoms] Custom harness \u2014 bring your own connector:
|
|
7174
7549
|
${customFacts(input)}`, wired: false, error: false };
|
|
@@ -7197,10 +7572,10 @@ function assertReadableCredential(absPath, deps) {
|
|
|
7197
7572
|
return { deviceId: json2.device_id, identityId: json2.identity_id, issuedAt };
|
|
7198
7573
|
}
|
|
7199
7574
|
function durableDir(home, slug) {
|
|
7200
|
-
return
|
|
7575
|
+
return join10(home, `securecoms-agent-${slug}`);
|
|
7201
7576
|
}
|
|
7202
7577
|
function durableCredentialPath(home, slug) {
|
|
7203
|
-
return
|
|
7578
|
+
return join10(durableDir(home, slug), `${slug}.json`);
|
|
7204
7579
|
}
|
|
7205
7580
|
function takeCustody(args2, deps) {
|
|
7206
7581
|
const dir = durableDir(args2.home, args2.slug);
|
|
@@ -7247,7 +7622,7 @@ function readSiblingDefs(dir, prefix, deps) {
|
|
|
7247
7622
|
for (const n of names) {
|
|
7248
7623
|
if (!n.startsWith(prefix)) continue;
|
|
7249
7624
|
try {
|
|
7250
|
-
out.push(deps.readFile(
|
|
7625
|
+
out.push(deps.readFile(join10(dir, n)));
|
|
7251
7626
|
} catch {
|
|
7252
7627
|
}
|
|
7253
7628
|
}
|
|
@@ -7443,29 +7818,29 @@ function renderStatusTable(rows) {
|
|
|
7443
7818
|
import {
|
|
7444
7819
|
chmodSync,
|
|
7445
7820
|
mkdirSync,
|
|
7446
|
-
readFileSync as
|
|
7821
|
+
readFileSync as readFileSync7,
|
|
7447
7822
|
renameSync,
|
|
7448
7823
|
unlinkSync,
|
|
7449
7824
|
writeFileSync as writeFileSync2
|
|
7450
7825
|
} from "node:fs";
|
|
7451
7826
|
import { randomUUID } from "node:crypto";
|
|
7452
|
-
import { join as
|
|
7827
|
+
import { join as join11 } from "node:path";
|
|
7453
7828
|
var REAL_FS = {
|
|
7454
7829
|
mkdir: (p, mode) => mkdirSync(p, { recursive: true, mode }),
|
|
7455
7830
|
chmod: (p, mode) => chmodSync(p, mode),
|
|
7456
7831
|
writeFile: (p, data, mode) => writeFileSync2(p, data, { mode }),
|
|
7457
7832
|
rename: (a, b) => renameSync(a, b),
|
|
7458
|
-
readFile: (p) =>
|
|
7833
|
+
readFile: (p) => readFileSync7(p, "utf8"),
|
|
7459
7834
|
unlink: (p) => unlinkSync(p)
|
|
7460
7835
|
};
|
|
7461
7836
|
function securecomsDir(home) {
|
|
7462
|
-
return
|
|
7837
|
+
return join11(home, ".securecoms");
|
|
7463
7838
|
}
|
|
7464
7839
|
function connectionsDir(home) {
|
|
7465
|
-
return
|
|
7840
|
+
return join11(securecomsDir(home), "connections");
|
|
7466
7841
|
}
|
|
7467
7842
|
function connectionFilePath(home, identityId) {
|
|
7468
|
-
return
|
|
7843
|
+
return join11(connectionsDir(home), `${identityId}.json`);
|
|
7469
7844
|
}
|
|
7470
7845
|
function newInstanceId() {
|
|
7471
7846
|
return randomUUID();
|
|
@@ -7580,7 +7955,7 @@ function realResolveDeps() {
|
|
|
7580
7955
|
}
|
|
7581
7956
|
function realPrepDeps() {
|
|
7582
7957
|
return {
|
|
7583
|
-
readFile: (p) =>
|
|
7958
|
+
readFile: (p) => readFileSync8(p, "utf8"),
|
|
7584
7959
|
writeFileMode: (p, data, mode) => writeFileSync3(p, data, { mode }),
|
|
7585
7960
|
mkdirp: (p, mode) => {
|
|
7586
7961
|
mkdirSync2(p, { recursive: true, mode: mode ?? 493 });
|
|
@@ -7589,7 +7964,7 @@ function realPrepDeps() {
|
|
|
7589
7964
|
realpath: (p) => realpathSync(p),
|
|
7590
7965
|
listDir: (p) => {
|
|
7591
7966
|
try {
|
|
7592
|
-
return
|
|
7967
|
+
return readdirSync2(p);
|
|
7593
7968
|
} catch {
|
|
7594
7969
|
return [];
|
|
7595
7970
|
}
|
|
@@ -7631,12 +8006,12 @@ function realReadFleet() {
|
|
|
7631
8006
|
home: homedir2(),
|
|
7632
8007
|
listDir: (p) => {
|
|
7633
8008
|
try {
|
|
7634
|
-
return
|
|
8009
|
+
return readdirSync2(p);
|
|
7635
8010
|
} catch {
|
|
7636
8011
|
return [];
|
|
7637
8012
|
}
|
|
7638
8013
|
},
|
|
7639
|
-
readFile: (p) =>
|
|
8014
|
+
readFile: (p) => readFileSync8(p, "utf8")
|
|
7640
8015
|
});
|
|
7641
8016
|
}
|
|
7642
8017
|
function runStatus() {
|
|
@@ -7656,40 +8031,56 @@ function runStatus() {
|
|
|
7656
8031
|
else process.stdout.write(renderStatusTable(rows));
|
|
7657
8032
|
process.exit(0);
|
|
7658
8033
|
}
|
|
7659
|
-
function realCopyConnector(srcDir, dstDir) {
|
|
8034
|
+
function realCopyConnector(srcDir, dstDir, harness) {
|
|
8035
|
+
const allowedExt = CONNECTOR_FILE_EXTENSIONS[harness];
|
|
7660
8036
|
mkdirSync2(dstDir, { recursive: true });
|
|
7661
|
-
for (const name of
|
|
7662
|
-
const srcPath =
|
|
7663
|
-
if (
|
|
7664
|
-
|
|
8037
|
+
for (const name of readdirSync2(srcDir)) {
|
|
8038
|
+
const srcPath = join12(srcDir, name);
|
|
8039
|
+
if (statSync3(srcPath).isDirectory()) {
|
|
8040
|
+
if (CONNECTOR_SKIP_DIRS.has(name)) continue;
|
|
8041
|
+
realCopyConnector(srcPath, join12(dstDir, name), harness);
|
|
7665
8042
|
continue;
|
|
7666
8043
|
}
|
|
7667
|
-
if (
|
|
7668
|
-
|
|
8044
|
+
if (name.includes(".test.")) continue;
|
|
8045
|
+
if (!allowedExt.some((ext) => name.endsWith(ext))) continue;
|
|
8046
|
+
writeFileSync3(join12(dstDir, name), readFileSync8(srcPath));
|
|
7669
8047
|
}
|
|
7670
8048
|
}
|
|
7671
8049
|
function realWriteBinding(dstDir, identityId) {
|
|
7672
8050
|
mkdirSync2(dstDir, { recursive: true });
|
|
7673
|
-
writeFileSync3(
|
|
8051
|
+
writeFileSync3(join12(dstDir, "securecoms.binding.json"), JSON.stringify({ identity_id: identityId }) + "\n", { mode: 384 });
|
|
7674
8052
|
}
|
|
7675
8053
|
function realReadBinding(dstDir) {
|
|
7676
8054
|
try {
|
|
7677
|
-
const parsed = JSON.parse(
|
|
8055
|
+
const parsed = JSON.parse(readFileSync8(join12(dstDir, "securecoms.binding.json"), "utf8"));
|
|
7678
8056
|
return parsed.identity_id ?? null;
|
|
7679
8057
|
} catch {
|
|
7680
8058
|
return null;
|
|
7681
8059
|
}
|
|
7682
8060
|
}
|
|
7683
8061
|
function realRemoveBinding(dstDir) {
|
|
7684
|
-
rmSync(
|
|
8062
|
+
rmSync(join12(dstDir, "securecoms.binding.json"), { force: true });
|
|
7685
8063
|
}
|
|
7686
8064
|
function realStatDir(p) {
|
|
7687
8065
|
try {
|
|
7688
|
-
return
|
|
8066
|
+
return statSync3(p).isDirectory();
|
|
7689
8067
|
} catch {
|
|
7690
8068
|
return false;
|
|
7691
8069
|
}
|
|
7692
8070
|
}
|
|
8071
|
+
function realReadHermesConfig(p) {
|
|
8072
|
+
try {
|
|
8073
|
+
return readFileSync8(p, "utf8");
|
|
8074
|
+
} catch {
|
|
8075
|
+
return null;
|
|
8076
|
+
}
|
|
8077
|
+
}
|
|
8078
|
+
function realBackupHermesConfig(p, originalText) {
|
|
8079
|
+
writeFileSync3(hermesConfigBackupPath(p, Date.now(), process.pid), originalText, "utf8");
|
|
8080
|
+
}
|
|
8081
|
+
function realWriteHermesConfig(p, newText) {
|
|
8082
|
+
writeFileSync3(p, newText, "utf8");
|
|
8083
|
+
}
|
|
7693
8084
|
function realWireArgv() {
|
|
7694
8085
|
if (process.argv.includes("--harness")) return process.argv;
|
|
7695
8086
|
const detected = detectHarness(homedir2(), realStatDir);
|
|
@@ -7706,8 +8097,12 @@ function realWireDeps() {
|
|
|
7706
8097
|
getHealth: realGetHealth,
|
|
7707
8098
|
log: (s) => process.stdout.write(s + "\n"),
|
|
7708
8099
|
// bin.ts's OWN import.meta.url — see connectorsSrcDir's doc comment in
|
|
7709
|
-
// wire.ts for why it must be the entry point's URL, not wire.ts's.
|
|
7710
|
-
|
|
8100
|
+
// wire.ts for why it must be the entry point's URL, not wire.ts's. Keyed
|
|
8101
|
+
// by harness (Task 6) so one WireDeps instance can wire either harness.
|
|
8102
|
+
connectorsSrcDir: (h) => connectorsSrcDir(import.meta.url, h),
|
|
8103
|
+
readHermesConfig: realReadHermesConfig,
|
|
8104
|
+
backupHermesConfig: realBackupHermesConfig,
|
|
8105
|
+
writeHermesConfig: realWriteHermesConfig
|
|
7711
8106
|
};
|
|
7712
8107
|
}
|
|
7713
8108
|
function runWireVerb(verb) {
|
|
@@ -7736,21 +8131,25 @@ async function runServiceVerb(verb) {
|
|
|
7736
8131
|
argOrEnv("--exec-path", "SECURECOMS_EXEC_PATH"),
|
|
7737
8132
|
realResolveNodeDeps()
|
|
7738
8133
|
);
|
|
8134
|
+
const installHarness = argOrEnv("--harness", "SECURECOMS_HARNESS");
|
|
7739
8135
|
const r = await orchestrateInstall({
|
|
7740
8136
|
credentialAbsPath: credAbs,
|
|
7741
8137
|
label,
|
|
7742
8138
|
explicitPort: portStr ? Number(portStr) : void 0,
|
|
7743
|
-
harness:
|
|
8139
|
+
harness: installHarness,
|
|
7744
8140
|
home: homedir2(),
|
|
7745
|
-
connectorsSrcDir: connectorsSrcDir(import.meta.url),
|
|
8141
|
+
connectorsSrcDir: connectorsSrcDir(import.meta.url, installHarness === "hermes" ? "hermes" : "openclaw"),
|
|
7746
8142
|
autoWireDeps: {
|
|
7747
8143
|
statDir: realStatDir,
|
|
7748
|
-
// reuse bin.ts:
|
|
8144
|
+
// reuse bin.ts:266
|
|
7749
8145
|
readBinding: realReadBinding,
|
|
7750
8146
|
copyConnector: realCopyConnector,
|
|
7751
|
-
// bin.ts:
|
|
8147
|
+
// bin.ts:225 — one copy impl
|
|
7752
8148
|
writeBinding: realWriteBinding,
|
|
7753
|
-
// bin.ts:
|
|
8149
|
+
// bin.ts:245 — writes {identity_id}
|
|
8150
|
+
readHermesConfig: realReadHermesConfig,
|
|
8151
|
+
backupHermesConfig: realBackupHermesConfig,
|
|
8152
|
+
writeHermesConfig: realWriteHermesConfig,
|
|
7754
8153
|
log: (s) => process.stderr.write(s + "\n")
|
|
7755
8154
|
},
|
|
7756
8155
|
prepDeps: realPrepDeps(),
|
|
@@ -7830,11 +8229,11 @@ async function main() {
|
|
|
7830
8229
|
`);
|
|
7831
8230
|
process.exit(1);
|
|
7832
8231
|
}
|
|
7833
|
-
const tokenFile = daemon.tokenFile ??
|
|
8232
|
+
const tokenFile = daemon.tokenFile ?? join12(dirname7(credentialPath), "bridge-token");
|
|
7834
8233
|
const token = resolveToken({ tokenFile, token: daemon.token });
|
|
7835
8234
|
const agent = new BridgeAgent(cfg);
|
|
7836
8235
|
await agent.start();
|
|
7837
|
-
const tls = daemon.tls ? { cert:
|
|
8236
|
+
const tls = daemon.tls ? { cert: readFileSync8(daemon.tlsCert, "utf8"), key: readFileSync8(daemon.tlsKey, "utf8") } : void 0;
|
|
7838
8237
|
const { server, closeAllSseStreams } = createHttpServer(agent, { token, allowOrigins: daemon.allowOrigins, tls, websocket: true });
|
|
7839
8238
|
const agentWs = attachAgentWebSocket(server, agent, { token, allowOrigins: daemon.allowOrigins });
|
|
7840
8239
|
let down = false;
|
|
@@ -7897,6 +8296,7 @@ export {
|
|
|
7897
8296
|
chooseInstallExecPath,
|
|
7898
8297
|
nodeMajorOk,
|
|
7899
8298
|
parseVerb,
|
|
8299
|
+
realCopyConnector,
|
|
7900
8300
|
slugFromCredentialPath,
|
|
7901
8301
|
tokenFingerprint
|
|
7902
8302
|
};
|