@securecoms/agent-bridge 0.30.1 → 0.30.3
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 +77 -6
- package/dist/bin.d.ts +20 -1
- package/dist/bin.js +413 -63
- package/dist/bridge-agent.d.ts +60 -0
- package/dist/config.d.ts +13 -0
- package/dist/consumer-grants.d.ts +15 -0
- package/dist/grants/binding.d.ts +30 -2
- package/dist/grants/manifest.d.ts +11 -1
- package/dist/grants/search-providers.d.ts +9 -0
- package/dist/http-server.d.ts +2 -0
- package/dist/index.js +266 -32
- package/dist/preflight-upgrade.d.cts +1 -2
- package/dist/preflight.cjs +52 -49
- package/dist/preflight.d.cts +14 -0
- package/dist/service/credential-arg.d.ts +23 -0
- package/dist/service/install.d.ts +7 -0
- package/dist/service/resolve-node.d.ts +1 -0
- package/dist/service/runtime-config-dir.d.ts +4 -0
- package/dist/version.d.ts +1 -1
- package/dist/ws-server.d.ts +1 -1
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -7556,7 +7556,7 @@ __export(import_bundle_exports, {
|
|
|
7556
7556
|
importModelBundle: () => importModelBundle
|
|
7557
7557
|
});
|
|
7558
7558
|
import { createHash as createHash6, randomBytes as randomBytes12 } from "node:crypto";
|
|
7559
|
-
import { readFileSync as
|
|
7559
|
+
import { readFileSync as readFileSync16, statSync as statSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
7560
7560
|
import { join as join24 } from "node:path";
|
|
7561
7561
|
async function importModelBundle(srcDir, root3, m) {
|
|
7562
7562
|
const verified = [];
|
|
@@ -7574,7 +7574,7 @@ async function importModelBundle(srcDir, root3, m) {
|
|
|
7574
7574
|
if (size !== f.size) throw mismatch;
|
|
7575
7575
|
let bytes;
|
|
7576
7576
|
try {
|
|
7577
|
-
bytes =
|
|
7577
|
+
bytes = readFileSync16(src);
|
|
7578
7578
|
} catch {
|
|
7579
7579
|
throw new ModelImportError(`${f.name} is missing from the import directory`);
|
|
7580
7580
|
}
|
|
@@ -8001,8 +8001,30 @@ function ackOnlyGreeting(agentName, callerName) {
|
|
|
8001
8001
|
|
|
8002
8002
|
// src/config.ts
|
|
8003
8003
|
var REQUIRED = ["api_key", "identity_id", "device_id", "signing_private_key", "enclave_id", "api_base"];
|
|
8004
|
+
var CONFIG_ERROR_MESSAGE = {
|
|
8005
|
+
credential_not_found: "credential file not found",
|
|
8006
|
+
credential_unreadable: "credential file could not be read",
|
|
8007
|
+
credential_not_json: "credential file is not valid JSON",
|
|
8008
|
+
credential_missing_field: "credential file missing required field",
|
|
8009
|
+
credential_voice_invalid: "credential file voice config invalid"
|
|
8010
|
+
};
|
|
8011
|
+
var ConfigError = class extends Error {
|
|
8012
|
+
constructor(reason, field, source) {
|
|
8013
|
+
super(CONFIG_ERROR_MESSAGE[reason] + (field ? `: ${field}` : ""));
|
|
8014
|
+
this.reason = reason;
|
|
8015
|
+
this.field = field;
|
|
8016
|
+
this.source = source;
|
|
8017
|
+
this.name = "ConfigError";
|
|
8018
|
+
}
|
|
8019
|
+
};
|
|
8004
8020
|
function loadConfig(opts) {
|
|
8005
|
-
|
|
8021
|
+
let raw;
|
|
8022
|
+
try {
|
|
8023
|
+
raw = readFileSync2(opts.credentialPath, "utf8");
|
|
8024
|
+
} catch (e) {
|
|
8025
|
+
const code = e?.code;
|
|
8026
|
+
throw new ConfigError(code === "ENOENT" ? "credential_not_found" : "credential_unreadable");
|
|
8027
|
+
}
|
|
8006
8028
|
try {
|
|
8007
8029
|
const mode = statSync(opts.credentialPath).mode & 511;
|
|
8008
8030
|
if (mode & 63) writeServiceLog("warn", "config.credential_permissions_unsafe");
|
|
@@ -8012,9 +8034,9 @@ function loadConfig(opts) {
|
|
|
8012
8034
|
try {
|
|
8013
8035
|
json2 = JSON.parse(raw);
|
|
8014
8036
|
} catch {
|
|
8015
|
-
throw new
|
|
8037
|
+
throw new ConfigError("credential_not_json");
|
|
8016
8038
|
}
|
|
8017
|
-
for (const k of REQUIRED) if (typeof json2[k] !== "string" || !json2[k]) throw new
|
|
8039
|
+
for (const k of REQUIRED) if (typeof json2[k] !== "string" || !json2[k]) throw new ConfigError("credential_missing_field", k);
|
|
8018
8040
|
const storePath = opts.storePath ?? join2(dirname2(opts.credentialPath), "bridge-state.db");
|
|
8019
8041
|
const ownerGrantPin = loadOwnerGrantPin({ credentialPath: opts.credentialPath, credentialJson: json2 });
|
|
8020
8042
|
writeServiceLog(ownerGrantPin.state === "ok" || ownerGrantPin.state === "no_pin" ? "info" : "warn", "authz.pin", {
|
|
@@ -8052,8 +8074,14 @@ function loadConfig(opts) {
|
|
|
8052
8074
|
let voice;
|
|
8053
8075
|
try {
|
|
8054
8076
|
voice = parseVoiceConfig(json2.voice, process.env);
|
|
8055
|
-
} catch
|
|
8056
|
-
|
|
8077
|
+
} catch {
|
|
8078
|
+
let source = "env";
|
|
8079
|
+
try {
|
|
8080
|
+
parseVoiceConfig(json2.voice, {});
|
|
8081
|
+
} catch {
|
|
8082
|
+
source = "file";
|
|
8083
|
+
}
|
|
8084
|
+
throw new ConfigError("credential_voice_invalid", void 0, source);
|
|
8057
8085
|
}
|
|
8058
8086
|
const modelSource = json2.memory?.model_source === "import_only" ? "import_only" : "download";
|
|
8059
8087
|
const enabledRaw = json2.memory?.enabled;
|
|
@@ -18169,7 +18197,13 @@ var CODES = [
|
|
|
18169
18197
|
// lookup missed and before any write, so a retry can never land: the
|
|
18170
18198
|
// pending commit is dropped instead of being resent forever. The same pair
|
|
18171
18199
|
// answers an application send into a deleted channel (#325 F5).
|
|
18172
|
-
{ status: 409, code: "channel_unavailable" }
|
|
18200
|
+
{ status: 409, code: "channel_unavailable" },
|
|
18201
|
+
// ADR-0023 B.1: an Add or Welcome into a humans-only enclave's group naming
|
|
18202
|
+
// a device that is not provably a person's. Refused after the replay lookup
|
|
18203
|
+
// missed and before any write, so nothing landed. Definite: the committer's
|
|
18204
|
+
// work cannot name an agent in a humans-only enclave (admission refuses
|
|
18205
|
+
// one), so dropping the commit starts no resend loop.
|
|
18206
|
+
{ status: 403, code: "humans_only_enclave" }
|
|
18173
18207
|
];
|
|
18174
18208
|
var DISTRIBUTE_DEFINITE_CODES = Object.freeze(CODES.map((c) => Object.freeze({ ...c })));
|
|
18175
18209
|
var DEFINITE_KEYS = new Set(DISTRIBUTE_DEFINITE_CODES.map((c) => `${c.status} ${c.code}`));
|
|
@@ -29848,7 +29882,7 @@ function loadPackageMeta(importMetaUrl) {
|
|
|
29848
29882
|
}
|
|
29849
29883
|
|
|
29850
29884
|
// src/bridge-agent.ts
|
|
29851
|
-
import { existsSync as existsSync5, promises as fsp, statSync as statSync5 } from "node:fs";
|
|
29885
|
+
import { existsSync as existsSync5, promises as fsp, readFileSync as readFileSync11, statSync as statSync5 } from "node:fs";
|
|
29852
29886
|
import { join as join19, dirname as dirname7 } from "node:path";
|
|
29853
29887
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
29854
29888
|
|
|
@@ -34363,6 +34397,15 @@ import {
|
|
|
34363
34397
|
writeFileSync
|
|
34364
34398
|
} from "node:fs";
|
|
34365
34399
|
import { basename as basename4, isAbsolute, join as join18 } from "node:path";
|
|
34400
|
+
|
|
34401
|
+
// src/grants/search-providers.ts
|
|
34402
|
+
var SEARCH_PROVIDERS = /* @__PURE__ */ new Set(["brave", "tavily", "parallel", "exa", "firecrawl"]);
|
|
34403
|
+
var OWNER_DM_SEARCH_DAILY_CAP_MAX = 2e3;
|
|
34404
|
+
function isSearchProvider(value) {
|
|
34405
|
+
return typeof value === "string" && SEARCH_PROVIDERS.has(value);
|
|
34406
|
+
}
|
|
34407
|
+
|
|
34408
|
+
// src/service/runtime-config-dir.ts
|
|
34366
34409
|
var MEMORY_FILE = "MEMORY.md";
|
|
34367
34410
|
var REAL_FS = {
|
|
34368
34411
|
mkdir: (p, mode) => mkdirSync5(p, { recursive: true, mode }),
|
|
@@ -34545,10 +34588,14 @@ function readLocalReport(home, identityId) {
|
|
|
34545
34588
|
const persona = raw && typeof raw === "object" && !Array.isArray(raw) ? Object.fromEntries(
|
|
34546
34589
|
Object.entries(raw).filter(([, v]) => typeof v === "string")
|
|
34547
34590
|
) : {};
|
|
34591
|
+
const capRaw = doc.owner_dm_search_cap_effective;
|
|
34592
|
+
const ownerDmCap = typeof capRaw === "number" && Number.isInteger(capRaw) && capRaw >= 0 && capRaw <= OWNER_DM_SEARCH_DAILY_CAP_MAX ? capRaw : void 0;
|
|
34548
34593
|
return {
|
|
34549
34594
|
persona,
|
|
34550
34595
|
has_search_key: hasSearchKey,
|
|
34551
34596
|
has_llm_key: hasLlmKey,
|
|
34597
|
+
has_extract_provider: doc.has_extract_provider === true,
|
|
34598
|
+
...ownerDmCap !== void 0 ? { owner_dm_search_cap_effective: ownerDmCap } : {},
|
|
34552
34599
|
legacy_memory: localLegacySources(doc).map(({ path: _path, ...source }) => source),
|
|
34553
34600
|
secret_paths: parseSecretPathsReport(doc.secret_paths)
|
|
34554
34601
|
};
|
|
@@ -34666,7 +34713,8 @@ import { sha256 as sha25611 } from "@noble/hashes/sha256";
|
|
|
34666
34713
|
import { ed25519 as ed255195 } from "@noble/curves/ed25519";
|
|
34667
34714
|
var TOOL_GRANTS_DOMAIN = "SECURECOMS-TOOL-GRANTS-V1:";
|
|
34668
34715
|
var MANIFEST_KNOWN_TOOLS = Object.freeze({
|
|
34669
|
-
owner_dm: Object.freeze(["web_search", "fetch_url", "http_request", "sql_query"]),
|
|
34716
|
+
owner_dm: Object.freeze(["web_search", "web_extract", "fetch_url", "http_request", "sql_query"]),
|
|
34717
|
+
// web_extract: owner DM only, never team_room (spec §5.4, D5)
|
|
34670
34718
|
team_room: Object.freeze(["web_search"])
|
|
34671
34719
|
});
|
|
34672
34720
|
function manifestSignedBytes(manifest) {
|
|
@@ -34741,7 +34789,11 @@ function parseConfig(v) {
|
|
|
34741
34789
|
}
|
|
34742
34790
|
if ("search" in v) {
|
|
34743
34791
|
const sr = v.search;
|
|
34744
|
-
if (!isPlainObject(sr) || !exactKeys(sr, ["provider"]
|
|
34792
|
+
if (!isPlainObject(sr) || !exactKeys(sr, ["provider"], ["owner_dm_daily_cap"]) || !isSearchProvider(sr.provider)) return null;
|
|
34793
|
+
if ("owner_dm_daily_cap" in sr) {
|
|
34794
|
+
const cap = sr.owner_dm_daily_cap;
|
|
34795
|
+
if (typeof cap !== "number" || !Number.isSafeInteger(cap) || cap < 0 || cap > OWNER_DM_SEARCH_DAILY_CAP_MAX) return null;
|
|
34796
|
+
}
|
|
34745
34797
|
}
|
|
34746
34798
|
const mem = v.memory;
|
|
34747
34799
|
if (!isPlainObject(mem) || !exactKeys(mem, ["mode"], ["recall_days"])) return null;
|
|
@@ -34751,7 +34803,12 @@ function parseConfig(v) {
|
|
|
34751
34803
|
return Object.freeze({
|
|
34752
34804
|
persona_sha256: v.persona_sha256,
|
|
34753
34805
|
...isPlainObject(v.llm) ? { llm: Object.freeze({ provider: v.llm.provider, model: v.llm.model }) } : {},
|
|
34754
|
-
...isPlainObject(v.search) ? {
|
|
34806
|
+
...isPlainObject(v.search) ? {
|
|
34807
|
+
search: Object.freeze({
|
|
34808
|
+
provider: v.search.provider,
|
|
34809
|
+
..."owner_dm_daily_cap" in v.search ? { owner_dm_daily_cap: v.search.owner_dm_daily_cap } : {}
|
|
34810
|
+
})
|
|
34811
|
+
} : {},
|
|
34755
34812
|
...isStringArray(v.tools_allow) ? { tools_allow: Object.freeze([...v.tools_allow]) } : {},
|
|
34756
34813
|
flags: Object.freeze([...v.flags]),
|
|
34757
34814
|
memory: Object.freeze({ mode: mem.mode, ..."recall_days" in mem ? { recall_days: mem.recall_days } : {} }),
|
|
@@ -34982,7 +35039,20 @@ function bindTargets(raw, signed, equals) {
|
|
|
34982
35039
|
}
|
|
34983
35040
|
return { written, mismatches };
|
|
34984
35041
|
}
|
|
34985
|
-
function
|
|
35042
|
+
function signedManifestTools(manifest, audience, searchProvidersCapable) {
|
|
35043
|
+
return MANIFEST_KNOWN_TOOLS[audience].filter((t) => (manifest.grants[audience] ?? []).includes(t) && (t !== "web_extract" || searchProvidersCapable));
|
|
35044
|
+
}
|
|
35045
|
+
function manifestPolicyFor(manifest, switches, searchProvidersCapable) {
|
|
35046
|
+
return {
|
|
35047
|
+
v: 1,
|
|
35048
|
+
manifest_tools: [.../* @__PURE__ */ new Set([
|
|
35049
|
+
...switches.ownerDm ? signedManifestTools(manifest, "owner_dm", searchProvidersCapable) : [],
|
|
35050
|
+
...switches.room ? signedManifestTools(manifest, "team_room", searchProvidersCapable) : []
|
|
35051
|
+
])].sort(),
|
|
35052
|
+
secret_path_mode: "enforce"
|
|
35053
|
+
};
|
|
35054
|
+
}
|
|
35055
|
+
function bindRuntimeConfig(manifest, payload, prior, switches = { ownerDm: true, room: true }, searchProvidersCapable = false) {
|
|
34986
35056
|
const mismatches = /* @__PURE__ */ new Set();
|
|
34987
35057
|
const cfg = manifest.config;
|
|
34988
35058
|
const out = {};
|
|
@@ -35011,6 +35081,7 @@ function bindRuntimeConfig(manifest, payload, prior, switches = { ownerDm: true,
|
|
|
35011
35081
|
for (const name of Object.keys(files)) {
|
|
35012
35082
|
if (name !== LLM_SOURCE && !PERSONA_FILES.includes(name)) mismatches.add("files");
|
|
35013
35083
|
}
|
|
35084
|
+
const ownerDmTools = signedManifestTools(manifest, "owner_dm", searchProvidersCapable);
|
|
35014
35085
|
const sj = isObj2(payload.skills_json) ? payload.skills_json : void 0;
|
|
35015
35086
|
const eps = bindTargets(sj?.endpoints, manifest.endpoints, endpointEquals);
|
|
35016
35087
|
const dbs = bindTargets(sj?.databases, manifest.databases, databaseEquals);
|
|
@@ -35028,14 +35099,20 @@ function bindRuntimeConfig(manifest, payload, prior, switches = { ownerDm: true,
|
|
|
35028
35099
|
appliedSearch = searchBlock;
|
|
35029
35100
|
} else {
|
|
35030
35101
|
mismatches.add("search");
|
|
35031
|
-
|
|
35102
|
+
const usablePrior = cfg.search !== void 0 && prior.search?.provider === cfg.search.provider ? prior.search : void 0;
|
|
35103
|
+
searchBlock = cfg.search === void 0 ? void 0 : usablePrior ?? Object.freeze({ provider: cfg.search.provider });
|
|
35032
35104
|
}
|
|
35033
35105
|
const skillsDoc = {
|
|
35034
35106
|
endpoints: eps.written,
|
|
35035
35107
|
databases: dbs.written,
|
|
35036
35108
|
flags: [...cfg.flags],
|
|
35037
35109
|
...deny !== void 0 || cfg.tools_allow !== void 0 ? { tools: { ...deny !== void 0 ? { deny } : {}, ...cfg.tools_allow !== void 0 ? { allow: [...cfg.tools_allow] } : {} } } : {},
|
|
35038
|
-
...searchBlock ? { search: searchBlock } : {}
|
|
35110
|
+
...searchBlock ? { search: searchBlock } : {},
|
|
35111
|
+
// D31: the marker the runtime keys D24 (iii) on; written under every manifest, true when config.search is signed.
|
|
35112
|
+
search_signed: cfg.search !== void 0,
|
|
35113
|
+
// D36, D39: the signed owner-DM cap, only when the owner-DM switch is on and the manifest grants an owner-DM tool;
|
|
35114
|
+
// otherwise none, so the runtime clamps a signed raise to the default. Never read from the payload.
|
|
35115
|
+
...cfg.search?.owner_dm_daily_cap !== void 0 && switches.ownerDm && ownerDmTools.length > 0 ? { owner_dm_search_cap: cfg.search.owner_dm_daily_cap } : {}
|
|
35039
35116
|
};
|
|
35040
35117
|
out["skills.json"] = JSON.stringify(skillsDoc, null, 2) + "\n";
|
|
35041
35118
|
const signedMemory = { mode: cfg.memory.mode, ...cfg.memory.recall_days !== void 0 ? { recall_days: cfg.memory.recall_days } : {} };
|
|
@@ -35066,12 +35143,7 @@ function bindRuntimeConfig(manifest, payload, prior, switches = { ownerDm: true,
|
|
|
35066
35143
|
mismatches.add("legacy_assign");
|
|
35067
35144
|
legacyDecisions = decisions.filter((d) => isObj2(d) && d.action === "delete");
|
|
35068
35145
|
}
|
|
35069
|
-
const
|
|
35070
|
-
const policy = {
|
|
35071
|
-
v: 1,
|
|
35072
|
-
manifest_tools: [.../* @__PURE__ */ new Set([...switches.ownerDm ? signedTools("owner_dm") : [], ...switches.room ? signedTools("team_room") : []])].sort(),
|
|
35073
|
-
secret_path_mode: "enforce"
|
|
35074
|
-
};
|
|
35146
|
+
const policy = manifestPolicyFor(manifest, switches, searchProvidersCapable);
|
|
35075
35147
|
return {
|
|
35076
35148
|
mismatches: CONFIG_BINDING_FIELDS.filter((f) => mismatches.has(f)).sort(),
|
|
35077
35149
|
targetMismatches: { endpoint: eps.mismatches, database: dbs.mismatches },
|
|
@@ -35210,6 +35282,8 @@ var TOOL_ROOM_CLASSES = Object.freeze({
|
|
|
35210
35282
|
current_time: Object.freeze(["room_safe_read"]),
|
|
35211
35283
|
echo: Object.freeze(["room_safe_read"]),
|
|
35212
35284
|
web_search: Object.freeze(["owner_credential"]),
|
|
35285
|
+
web_extract: Object.freeze(["owner_credential", "egress"]),
|
|
35286
|
+
// D5: spends the owner's key and sends URLs out
|
|
35213
35287
|
fetch_url: Object.freeze(["egress"]),
|
|
35214
35288
|
sql_query: Object.freeze(["owner_credential"]),
|
|
35215
35289
|
http_request: Object.freeze(["owner_credential", "egress", "side_effect"])
|
|
@@ -35631,6 +35705,8 @@ function hexToBytes4(h) {
|
|
|
35631
35705
|
return new Uint8Array(Buffer.from(h, "hex"));
|
|
35632
35706
|
}
|
|
35633
35707
|
var BOOT_DRAIN_WAIT_MS_DEFAULT = 6e4;
|
|
35708
|
+
var SEARCH_CAPABILITY_REFETCH_DEBOUNCE_MS = 1e3;
|
|
35709
|
+
var SEARCH_CAPABILITY_REFETCH_MAX_WAIT_MS = 1e4;
|
|
35634
35710
|
var BootDrainRotationRefusedError = class extends Error {
|
|
35635
35711
|
constructor() {
|
|
35636
35712
|
super("key-package rotation refused: the boot drain has not settled");
|
|
@@ -35826,6 +35902,8 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
35826
35902
|
this.actionClock = deps.actionThrottleNow ?? Date.now;
|
|
35827
35903
|
this.rng = deps.rng ?? Math.random;
|
|
35828
35904
|
this.bootDrainWaitMs = deps.bootDrainWaitMs ?? BOOT_DRAIN_WAIT_MS_DEFAULT;
|
|
35905
|
+
this.searchCapabilityRefetchDebounceMs = deps.searchCapabilityRefetchDebounceMs ?? SEARCH_CAPABILITY_REFETCH_DEBOUNCE_MS;
|
|
35906
|
+
this.searchCapabilityRefetchMaxWaitMs = deps.searchCapabilityRefetchMaxWaitMs ?? SEARCH_CAPABILITY_REFETCH_MAX_WAIT_MS;
|
|
35829
35907
|
this.replySleep = deps.replySleep ?? ((ms) => new Promise((resolve4) => {
|
|
35830
35908
|
const t = setTimeout(resolve4, ms);
|
|
35831
35909
|
t.unref?.();
|
|
@@ -36264,6 +36342,8 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36264
36342
|
bootSweepTimer;
|
|
36265
36343
|
rng;
|
|
36266
36344
|
bootDrainWaitMs;
|
|
36345
|
+
searchCapabilityRefetchDebounceMs;
|
|
36346
|
+
searchCapabilityRefetchMaxWaitMs;
|
|
36267
36347
|
/** Resolved (and cleared) each time an ingest cycle settles: see start(). */
|
|
36268
36348
|
ingestSettledWaiters = [];
|
|
36269
36349
|
/** The boot key-package publish, when start() had to defer it. Test seam. */
|
|
@@ -36709,9 +36789,14 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36709
36789
|
}
|
|
36710
36790
|
const skillsJson = payload.skills_json ?? {};
|
|
36711
36791
|
if (version && inForce === null) {
|
|
36792
|
+
let asDelivered = skillsJson;
|
|
36793
|
+
if (skillsJson !== null && typeof skillsJson === "object" && !Array.isArray(skillsJson)) {
|
|
36794
|
+
const { owner_dm_search_cap: _cap, search_signed: _signed, ...rest } = skillsJson;
|
|
36795
|
+
asDelivered = rest;
|
|
36796
|
+
}
|
|
36712
36797
|
this.grantWriters.writeManagedFiles(this.home, this.cfg.identityId, {
|
|
36713
36798
|
...payload.files ?? {},
|
|
36714
|
-
"skills.json": `${JSON.stringify(
|
|
36799
|
+
"skills.json": `${JSON.stringify(asDelivered, null, 2)}
|
|
36715
36800
|
`
|
|
36716
36801
|
});
|
|
36717
36802
|
}
|
|
@@ -36766,7 +36851,7 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36766
36851
|
}
|
|
36767
36852
|
let bound;
|
|
36768
36853
|
try {
|
|
36769
|
-
bound = this.grantWriters.bindRuntimeConfig(manifest, payload, { search: prior }, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants });
|
|
36854
|
+
bound = this.grantWriters.bindRuntimeConfig(manifest, payload, { search: prior }, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants }, this.searchProvidersCapable());
|
|
36770
36855
|
} catch (e) {
|
|
36771
36856
|
bound = null;
|
|
36772
36857
|
return this.bindFailed(manifest, payload.memory, e);
|
|
@@ -36785,9 +36870,9 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36785
36870
|
return true;
|
|
36786
36871
|
}
|
|
36787
36872
|
try {
|
|
36788
|
-
this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, bound.policy);
|
|
36789
36873
|
this.grantWriters.writeManagedFiles(this.home, this.cfg.identityId, bound.files);
|
|
36790
36874
|
if (manifest.config.llm === void 0) this.grantWriters.removeManagedLlmSource(this.home, this.cfg.identityId);
|
|
36875
|
+
this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, manifestPolicyFor(manifest, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants }, this.searchProvidersCapable()));
|
|
36791
36876
|
} catch (e) {
|
|
36792
36877
|
this.manifestBinding = null;
|
|
36793
36878
|
throw e;
|
|
@@ -36840,6 +36925,7 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36840
36925
|
/** The report half of refetchRuntimeConfigOnce (split out for the plan 4 Task 11 bind_failed path). */
|
|
36841
36926
|
async postRuntimeConfigReport(base, version, skillsJson) {
|
|
36842
36927
|
const local = readLocalReport(this.home, this.cfg.identityId);
|
|
36928
|
+
const reportedSearchProviders = this.searchProvidersCapable();
|
|
36843
36929
|
const report = {
|
|
36844
36930
|
applied_version: version,
|
|
36845
36931
|
paths: checkConfiguredPaths(skillsJson),
|
|
@@ -36854,6 +36940,13 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36854
36940
|
// to the backend. The backend needs it to generate a truthful tool
|
|
36855
36941
|
// paragraph, so it rides along here.
|
|
36856
36942
|
has_search_key: local?.has_search_key ?? false,
|
|
36943
|
+
// L-2 (spec §7.9): only the runtime knows whether an extract-capable provider key is readable; never derived
|
|
36944
|
+
// from has_search_key. An older runtime omits it: false.
|
|
36945
|
+
// Final review I-2 (controller ruling): AND the current search_providers_v2, so the web never shows extract while
|
|
36946
|
+
// the bridge withholds web_extract from the runtime.
|
|
36947
|
+
has_extract_provider: (local?.has_extract_provider ?? false) && reportedSearchProviders,
|
|
36948
|
+
// D39: the owner-DM daily cap the runtime enforces, passed through when it reported a valid one; never the signed value.
|
|
36949
|
+
...local?.owner_dm_search_cap_effective !== void 0 ? { owner_dm_search_cap_effective: local.owner_dm_search_cap_effective } : {},
|
|
36857
36950
|
// Same idea, for the BYOK LLM key file: only the runtime can see
|
|
36858
36951
|
// whether one is configured in its own environment.
|
|
36859
36952
|
has_llm_key: local?.has_llm_key ?? false,
|
|
@@ -36893,7 +36986,11 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36893
36986
|
// Plan 4 Task 15 (OQ-5): sorted, unique enums; capability is the latest hello's class.
|
|
36894
36987
|
config_binding_mismatches: [...this.configBindingMismatches],
|
|
36895
36988
|
target_mismatches: ["database", "endpoint"].filter((k) => this.targetMismatches[k] > 0),
|
|
36896
|
-
capability: this.lastConsumerCapability
|
|
36989
|
+
capability: this.lastConsumerCapability,
|
|
36990
|
+
// D28 (§11.1 step 3): a separate key, never a value in the capability enum. False with no runtime attached.
|
|
36991
|
+
search_providers_v2: reportedSearchProviders,
|
|
36992
|
+
// D19 item 4: content-free; the signed provider is one the attached runtime(s) cannot honour.
|
|
36993
|
+
search_provider_unsupported: this.searchProviderUnsupported(reportedSearchProviders)
|
|
36897
36994
|
}
|
|
36898
36995
|
};
|
|
36899
36996
|
const reportedVerifyFailures = this.ownerVerifyFailures;
|
|
@@ -36914,6 +37011,7 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36914
37011
|
return;
|
|
36915
37012
|
}
|
|
36916
37013
|
this.ownerVerifyFailures = Math.max(0, this.ownerVerifyFailures - reportedVerifyFailures);
|
|
37014
|
+
this.lastReportedSearchProviders = reportedSearchProviders;
|
|
36917
37015
|
if (version) writeServiceLog("info", "runtime_config.applied", { version });
|
|
36918
37016
|
else writeServiceLog("info", "runtime_config.legacy_memory_reported");
|
|
36919
37017
|
}
|
|
@@ -36983,6 +37081,7 @@ var BridgeAgent = class _BridgeAgent {
|
|
|
36983
37081
|
this.startedAt = Date.now();
|
|
36984
37082
|
await this.restoreRuntimeInbox();
|
|
36985
37083
|
await this.loadToolGrantState();
|
|
37084
|
+
this.recomputeManifestPolicy(false);
|
|
36986
37085
|
try {
|
|
36987
37086
|
this.store.pruneRoomSearchCap(utcDayOf(Date.now()));
|
|
36988
37087
|
} catch (e) {
|
|
@@ -42774,6 +42873,115 @@ ${channelId}`;
|
|
|
42774
42873
|
manifestConsumerAttached() {
|
|
42775
42874
|
return this.manifestConsumer !== null;
|
|
42776
42875
|
}
|
|
42876
|
+
/**
|
|
42877
|
+
* D28 (B-4): every socket that said hello, with whether its latest hello declared search_providers_v2 on the
|
|
42878
|
+
* manifest_grants_v1 predicate (ws-server.ts). The agent value is the AND over all of them, absent (false) with
|
|
42879
|
+
* none: unlike manifestConsumer, never newest wins. The per-delivery filter judges each socket by its own hello.
|
|
42880
|
+
*/
|
|
42881
|
+
searchProvidersSockets = /* @__PURE__ */ new Map();
|
|
42882
|
+
/**
|
|
42883
|
+
* The D28 value the files were last recomputed for; null until the first socket observation, so that observation
|
|
42884
|
+
* always recomputes (final review I-1), even when it is non-capable.
|
|
42885
|
+
*/
|
|
42886
|
+
searchProvidersState = null;
|
|
42887
|
+
searchCapabilityRefetchTimer = null;
|
|
42888
|
+
/** When the current burst's first change happened (valid while the timer is set). */
|
|
42889
|
+
searchCapabilityBurstStart = 0;
|
|
42890
|
+
/** The D28 value in the last report posted (null until one is); a change needs a refetch to reach the web. */
|
|
42891
|
+
lastReportedSearchProviders = null;
|
|
42892
|
+
noteSearchProvidersSocket(token, declared) {
|
|
42893
|
+
this.searchProvidersSockets.set(token, declared);
|
|
42894
|
+
this.onSearchProvidersMaybeChanged();
|
|
42895
|
+
}
|
|
42896
|
+
clearSearchProvidersSocket(token) {
|
|
42897
|
+
if (this.searchProvidersSockets.delete(token)) this.onSearchProvidersMaybeChanged();
|
|
42898
|
+
}
|
|
42899
|
+
/** D28: every attached runtime socket declared search_providers_v2; no socket attached means absent (false). */
|
|
42900
|
+
searchProvidersCapable() {
|
|
42901
|
+
if (this.searchProvidersSockets.size === 0) return false;
|
|
42902
|
+
for (const v of this.searchProvidersSockets.values()) if (!v) return false;
|
|
42903
|
+
return true;
|
|
42904
|
+
}
|
|
42905
|
+
/**
|
|
42906
|
+
* D19 item 3 (B-6): the search provider signed in the stored verified manifest in force (a pin loaded and a good
|
|
42907
|
+
* manifest), never the server payload; undefined with no pin, no manifest, or no signed search block. The
|
|
42908
|
+
* per-delivery filter (consumer-grants.ts withSearchCapability) and the S-28 report reason both read this.
|
|
42909
|
+
*/
|
|
42910
|
+
signedSearchProvider() {
|
|
42911
|
+
const pin = this.cfg.ownerGrantPin?.pin ?? null;
|
|
42912
|
+
return pin !== null ? this.toolGrantManifest?.config.search?.provider : void 0;
|
|
42913
|
+
}
|
|
42914
|
+
/** D19 item 4, D34: the in-force signed provider is not brave, and the attached runtime(s) lack the capability. */
|
|
42915
|
+
searchProviderUnsupported(capable) {
|
|
42916
|
+
if (capable) return false;
|
|
42917
|
+
const provider = this.signedSearchProvider();
|
|
42918
|
+
return provider !== void 0 && provider !== "brave";
|
|
42919
|
+
}
|
|
42920
|
+
/**
|
|
42921
|
+
* D35 (P-4): on a capability change, rewrite manifest-policy.json at once from the stored manifest, the kill
|
|
42922
|
+
* switches and the new capability, then a debounced refetch (one per burst) only when needed.
|
|
42923
|
+
*/
|
|
42924
|
+
onSearchProvidersMaybeChanged() {
|
|
42925
|
+
const capable = this.searchProvidersCapable();
|
|
42926
|
+
const prev = this.searchProvidersState;
|
|
42927
|
+
if (capable === prev) return;
|
|
42928
|
+
this.searchProvidersState = capable;
|
|
42929
|
+
this.recomputeManifestPolicy(capable);
|
|
42930
|
+
if (this.closingDown) return;
|
|
42931
|
+
if (prev === null && !capable) return;
|
|
42932
|
+
const now = Date.now();
|
|
42933
|
+
if (this.searchCapabilityRefetchTimer !== null) clearTimeout(this.searchCapabilityRefetchTimer);
|
|
42934
|
+
else this.searchCapabilityBurstStart = now;
|
|
42935
|
+
const delay = Math.max(0, Math.min(this.searchCapabilityRefetchDebounceMs, this.searchCapabilityBurstStart + this.searchCapabilityRefetchMaxWaitMs - now));
|
|
42936
|
+
this.searchCapabilityRefetchTimer = setTimeout(() => {
|
|
42937
|
+
this.searchCapabilityRefetchTimer = null;
|
|
42938
|
+
if (this.searchCapabilityRefetchNeeded()) void this.refetchRuntimeConfig();
|
|
42939
|
+
}, delay);
|
|
42940
|
+
this.searchCapabilityRefetchTimer.unref?.();
|
|
42941
|
+
}
|
|
42942
|
+
/**
|
|
42943
|
+
* A refetch re-renders skills.json (owner_dm_search_cap counts web_extract only with the capability, D39) and posts
|
|
42944
|
+
* the report. Needed under a manifest in force, or when the reported value is stale. Never before the boot fetch.
|
|
42945
|
+
*/
|
|
42946
|
+
searchCapabilityRefetchNeeded() {
|
|
42947
|
+
if (!this.bootConfigSettled || this.closingDown) return false;
|
|
42948
|
+
const inForce = (this.cfg.ownerGrantPin?.pin ?? null) !== null && this.toolGrantManifest !== null;
|
|
42949
|
+
return inForce || this.lastReportedSearchProviders !== this.searchProvidersCapable();
|
|
42950
|
+
}
|
|
42951
|
+
/**
|
|
42952
|
+
* D35: the local recompute. Widening (web_extract back in) only while the binding for the in-force manifest stands,
|
|
42953
|
+
* so a policy is never written ahead of its skills.json. Narrowing never waits: with no binding in force, a file that
|
|
42954
|
+
* lists web_extract loses it at once, so an older runtime never reads it as unreadable (which drops every grant).
|
|
42955
|
+
* Content-free; a write failure is logged and the debounced refetch tries again.
|
|
42956
|
+
*/
|
|
42957
|
+
recomputeManifestPolicy(capable) {
|
|
42958
|
+
const pin = this.cfg.ownerGrantPin?.pin ?? null;
|
|
42959
|
+
const manifest = pin !== null ? this.toolGrantManifest : null;
|
|
42960
|
+
try {
|
|
42961
|
+
if (manifest !== null && this.manifestBinding !== null && this.manifestBinding.counter === manifest.counter) {
|
|
42962
|
+
this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, manifestPolicyFor(manifest, { ownerDm: this.ownerDmManifestGrants, room: this.roomManifestGrants }, capable));
|
|
42963
|
+
return;
|
|
42964
|
+
}
|
|
42965
|
+
if (capable) return;
|
|
42966
|
+
const path = join19(runtimeConfigDir(this.home, this.cfg.identityId), MANIFEST_POLICY_FILE);
|
|
42967
|
+
if (!existsSync5(path)) return;
|
|
42968
|
+
let doc;
|
|
42969
|
+
try {
|
|
42970
|
+
doc = JSON.parse(readFileSync11(path, "utf8"));
|
|
42971
|
+
} catch {
|
|
42972
|
+
return;
|
|
42973
|
+
}
|
|
42974
|
+
const tools = doc?.manifest_tools;
|
|
42975
|
+
if (!Array.isArray(tools) || !tools.includes("web_extract")) return;
|
|
42976
|
+
this.grantWriters.writeManifestPolicy(this.home, this.cfg.identityId, {
|
|
42977
|
+
v: 1,
|
|
42978
|
+
manifest_tools: tools.filter((t) => typeof t === "string" && t !== "web_extract"),
|
|
42979
|
+
secret_path_mode: "enforce"
|
|
42980
|
+
});
|
|
42981
|
+
} catch (e) {
|
|
42982
|
+
writeServiceLog("warn", "authz.search_capability", { state: ServiceLogEnum.of("policy_write_failed", ["policy_write_failed"]) }, e);
|
|
42983
|
+
}
|
|
42984
|
+
}
|
|
42777
42985
|
noteConsumerCapability(state) {
|
|
42778
42986
|
this.lastConsumerCapability = state;
|
|
42779
42987
|
if (state !== "ok") {
|
|
@@ -42965,6 +43173,8 @@ ${channelId}`;
|
|
|
42965
43173
|
this.supersedeRecheckTimers.clear();
|
|
42966
43174
|
if (this.redeliveryTimer !== null) clearTimeout(this.redeliveryTimer);
|
|
42967
43175
|
this.redeliveryTimer = null;
|
|
43176
|
+
if (this.searchCapabilityRefetchTimer !== null) clearTimeout(this.searchCapabilityRefetchTimer);
|
|
43177
|
+
this.searchCapabilityRefetchTimer = null;
|
|
42968
43178
|
this.pingHandle?.close();
|
|
42969
43179
|
if (this.safetyTimer) clearInterval(this.safetyTimer);
|
|
42970
43180
|
if (this.runtimeConfigTimer) clearInterval(this.runtimeConfigTimer);
|
|
@@ -43075,11 +43285,11 @@ ${channelId}`;
|
|
|
43075
43285
|
|
|
43076
43286
|
// src/auth.ts
|
|
43077
43287
|
import { randomBytes as randomBytes11, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
43078
|
-
import { existsSync as existsSync6, readFileSync as
|
|
43288
|
+
import { existsSync as existsSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync2 } from "node:fs";
|
|
43079
43289
|
function resolveToken(opts) {
|
|
43080
43290
|
if (opts.token) return opts.token;
|
|
43081
43291
|
const f = opts.tokenFile;
|
|
43082
|
-
if (f && existsSync6(f)) return
|
|
43292
|
+
if (f && existsSync6(f)) return readFileSync12(f, "utf8").trim();
|
|
43083
43293
|
const tok = randomBytes11(32).toString("base64url");
|
|
43084
43294
|
if (f) writeFileSync2(f, tok, { mode: 384 });
|
|
43085
43295
|
return tok;
|
|
@@ -43180,6 +43390,7 @@ function withConsumerGrants(m, perToolConsumer) {
|
|
|
43180
43390
|
return { ...m, authorization: Object.freeze({ ...rest, ...stripGrants ? { tool_grants: NO_GRANTS } : {} }) };
|
|
43181
43391
|
}
|
|
43182
43392
|
var MANIFEST_GRANTS_FEATURE = "manifest_grants_v1";
|
|
43393
|
+
var SEARCH_PROVIDERS_FEATURE = "search_providers_v2";
|
|
43183
43394
|
var CONSTANT_TOOL_NAMES = Object.freeze({
|
|
43184
43395
|
owner_dm: Object.freeze(["current_time", "web_search"]),
|
|
43185
43396
|
team_room: Object.freeze(["current_time"])
|
|
@@ -43202,6 +43413,15 @@ function withHarnessGrants(m, withheld) {
|
|
|
43202
43413
|
if (!a || a.tool_grants.length === 0) return m;
|
|
43203
43414
|
return { ...m, authorization: Object.freeze({ ...a, tool_grants: NO_GRANTS }) };
|
|
43204
43415
|
}
|
|
43416
|
+
function withSearchCapability(m, consumerHasFeature, signedProvider) {
|
|
43417
|
+
if (consumerHasFeature) return m;
|
|
43418
|
+
const a = m.authorization;
|
|
43419
|
+
if (!a) return m;
|
|
43420
|
+
const stripSearch = signedProvider !== void 0 && signedProvider !== "brave";
|
|
43421
|
+
const kept = a.tool_grants.filter((g) => g.tool !== "web_extract" && !(stripSearch && g.tool === "web_search"));
|
|
43422
|
+
if (kept.length === a.tool_grants.length) return m;
|
|
43423
|
+
return { ...m, authorization: Object.freeze({ ...a, tool_grants: kept.length === 0 ? NO_GRANTS : Object.freeze(kept) }) };
|
|
43424
|
+
}
|
|
43205
43425
|
|
|
43206
43426
|
// src/http-server.ts
|
|
43207
43427
|
function writeSse(res, msgs, onWriteError) {
|
|
@@ -43224,7 +43444,8 @@ data: ${JSON.stringify(msgs[i])}
|
|
|
43224
43444
|
}
|
|
43225
43445
|
function deliverSseBatch(res, batch, agent, onWriteError) {
|
|
43226
43446
|
const withheld = agent.harnessGrantsWithheld();
|
|
43227
|
-
const
|
|
43447
|
+
const provider = agent.signedSearchProvider();
|
|
43448
|
+
const left3 = writeSse(res, batch.map((m) => withSearchCapability(withManifestCapability(withHarnessGrants(withConsumerGrants(m, false), withheld), false), false, provider)), onWriteError);
|
|
43228
43449
|
const leftIds = new Set(left3.map((message) => message.message_id));
|
|
43229
43450
|
agent.ackRuntimeMessages(batch.filter((message) => !leftIds.has(message.message_id)).map((message) => message.message_id));
|
|
43230
43451
|
if (left3.length) agent.requeueFront(batch.filter((message) => leftIds.has(message.message_id)));
|
|
@@ -43495,7 +43716,10 @@ function createHttpServer(agent, opts) {
|
|
|
43495
43716
|
if (method === "GET" && path === "/v1/messages") {
|
|
43496
43717
|
const channel = url.searchParams.get("channel_id") ?? void 0;
|
|
43497
43718
|
const wait = Math.max(0, Math.min(50, Number(url.searchParams.get("wait") ?? 0)));
|
|
43498
|
-
|
|
43719
|
+
const drained = await agent.drain(channel, wait);
|
|
43720
|
+
const withheld = agent.harnessGrantsWithheld();
|
|
43721
|
+
const provider = agent.signedSearchProvider();
|
|
43722
|
+
return json(res, 200, drained.map((m) => withSearchCapability(withManifestCapability(withHarnessGrants(withConsumerGrants(m, false), withheld), false), false, provider)));
|
|
43499
43723
|
}
|
|
43500
43724
|
if (method === "GET" && path === "/v1/events") {
|
|
43501
43725
|
const channel = url.searchParams.get("channel_id") ?? void 0;
|
|
@@ -43669,6 +43893,7 @@ function attachAgentWebSocket(server, agent, opts) {
|
|
|
43669
43893
|
consumer: "unknown",
|
|
43670
43894
|
perToolGrants: false,
|
|
43671
43895
|
manifestGrants: false,
|
|
43896
|
+
searchProviders: false,
|
|
43672
43897
|
// helloWaitMs 0 keeps today's synchronous initial flush exactly (the opt-out the existing tests use).
|
|
43673
43898
|
deliveryOpen: helloWaitMs <= 0,
|
|
43674
43899
|
deliveryToken: /* @__PURE__ */ new Map(),
|
|
@@ -43722,9 +43947,13 @@ function attachAgentWebSocket(server, agent, opts) {
|
|
|
43722
43947
|
continue;
|
|
43723
43948
|
}
|
|
43724
43949
|
const out = withConsumerNotes(
|
|
43725
|
-
|
|
43726
|
-
|
|
43727
|
-
|
|
43950
|
+
withSearchCapability(
|
|
43951
|
+
withManifestCapability(
|
|
43952
|
+
withHarnessGrants(withConsumerGrants(batch[i], session.perToolGrants), !session.perToolGrants && agent.harnessGrantsWithheld()),
|
|
43953
|
+
session.manifestGrants
|
|
43954
|
+
),
|
|
43955
|
+
session.searchProviders,
|
|
43956
|
+
agent.signedSearchProvider()
|
|
43728
43957
|
),
|
|
43729
43958
|
batch[i],
|
|
43730
43959
|
session,
|
|
@@ -43760,6 +43989,7 @@ function attachAgentWebSocket(server, agent, opts) {
|
|
|
43760
43989
|
const cleanup = () => {
|
|
43761
43990
|
if (cleaned) return;
|
|
43762
43991
|
cleaned = true;
|
|
43992
|
+
session.closed = true;
|
|
43763
43993
|
if (helloTimer) clearTimeout(helloTimer);
|
|
43764
43994
|
agent.noteSessionClose();
|
|
43765
43995
|
unsubscribe();
|
|
@@ -43780,6 +44010,7 @@ function attachAgentWebSocket(server, agent, opts) {
|
|
|
43780
44010
|
if (session.harness) agent.clearHarnessReport(session.harness);
|
|
43781
44011
|
if (session.perToolGrants) agent.clearPerToolConsumer(session);
|
|
43782
44012
|
if (session.manifestGrants) agent.clearManifestConsumer(session);
|
|
44013
|
+
agent.clearSearchProvidersSocket(session);
|
|
43783
44014
|
};
|
|
43784
44015
|
current = { sock, cleanup, sweepStale };
|
|
43785
44016
|
sock.on("close", cleanup);
|
|
@@ -43855,6 +44086,7 @@ async function handleInbound(sock, agent, data, inflight, flush, session) {
|
|
|
43855
44086
|
return;
|
|
43856
44087
|
}
|
|
43857
44088
|
if (m.type === "hello") {
|
|
44089
|
+
if (session.closed) return;
|
|
43858
44090
|
const report = m.harness === void 0 ? null : parseHarnessReport(m.harness);
|
|
43859
44091
|
if (report) {
|
|
43860
44092
|
session.harness = report;
|
|
@@ -43878,6 +44110,8 @@ async function handleInbound(sock, agent, data, inflight, flush, session) {
|
|
|
43878
44110
|
agent.clearManifestConsumer(session);
|
|
43879
44111
|
}
|
|
43880
44112
|
agent.noteConsumerCapability(manifest ? "ok" : session.harnessClaimed ? "harness_consumer" : "missing");
|
|
44113
|
+
session.searchProviders = session.consumer === "runtime" && session.harnessClaimed !== true && perTool && Array.isArray(m.features) && m.features.includes(SEARCH_PROVIDERS_FEATURE);
|
|
44114
|
+
agent.noteSearchProvidersSocket(session, session.searchProviders);
|
|
43881
44115
|
if (session.consumer === "runtime" && session.harnessClaimed !== true && Array.isArray(m.features) && m.features.includes(MEMORY_NOTES_FEATURE)) {
|
|
43882
44116
|
agent.noteNotesConsumer();
|
|
43883
44117
|
}
|
|
@@ -43885,7 +44119,7 @@ async function handleInbound(sock, agent, data, inflight, flush, session) {
|
|
|
43885
44119
|
if (session.recallConsumer) agent.noteRecallConsumer();
|
|
43886
44120
|
session.unifiedConsumer = agent.unifiedFeatureOn() && session.consumer === "runtime" && session.harnessClaimed !== true && Array.isArray(m.features) && m.features.includes(MEMORY_UNIFIED_FEATURE);
|
|
43887
44121
|
if (session.unifiedConsumer) agent.noteUnifiedConsumer();
|
|
43888
|
-
sendJson(sock, { type: "hello", features: ["nack", MEMORY_NOTES_FEATURE, MEMORY_RECALL_FEATURE, ...agent.unifiedFeatureOn() ? [MEMORY_UNIFIED_FEATURE] : [], MANIFEST_GRANTS_FEATURE] });
|
|
44122
|
+
sendJson(sock, { type: "hello", features: ["nack", MEMORY_NOTES_FEATURE, MEMORY_RECALL_FEATURE, ...agent.unifiedFeatureOn() ? [MEMORY_UNIFIED_FEATURE] : [], MANIFEST_GRANTS_FEATURE, SEARCH_PROVIDERS_FEATURE] });
|
|
43889
44123
|
if (!session.deliveryOpen) {
|
|
43890
44124
|
session.deliveryOpen = true;
|
|
43891
44125
|
flush();
|
|
@@ -44111,7 +44345,7 @@ async function handleInbound(sock, agent, data, inflight, flush, session) {
|
|
|
44111
44345
|
var LOWER_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
44112
44346
|
|
|
44113
44347
|
// src/bin.ts
|
|
44114
|
-
import { readFileSync as
|
|
44348
|
+
import { readFileSync as readFileSync17, mkdirSync as mkdirSync8, writeFileSync as writeFileSync5, rmSync as rmSync8, existsSync as existsSync7, realpathSync as realpathSync3, readdirSync as readdirSync4, statSync as statSync8, chmodSync as chmodSync6 } from "node:fs";
|
|
44115
44349
|
import { join as join25, dirname as dirname13, isAbsolute as isAbsolute3, resolve as resolve3 } from "node:path";
|
|
44116
44350
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
44117
44351
|
import { homedir as homedir5, userInfo } from "node:os";
|
|
@@ -44197,7 +44431,7 @@ function slugify(s) {
|
|
|
44197
44431
|
function deriveServiceConfig(input) {
|
|
44198
44432
|
if (isVersionEmbeddedNodePath(input.execPath)) {
|
|
44199
44433
|
throw new Error(
|
|
44200
|
-
`
|
|
44434
|
+
`The Node at ${input.execPath} moves when it is upgraded, so the background service can't use it. Install Node ${REQUIRED_NODE_MAJOR} from https://nodejs.org, then run the install command again.`
|
|
44201
44435
|
);
|
|
44202
44436
|
}
|
|
44203
44437
|
const stem = input.label ?? basename5(input.credentialPath, extname(input.credentialPath));
|
|
@@ -44278,6 +44512,26 @@ WantedBy=default.target
|
|
|
44278
44512
|
// src/service/install.ts
|
|
44279
44513
|
var ServiceUnsupportedError = class extends Error {
|
|
44280
44514
|
};
|
|
44515
|
+
var PREVIOUS_EXIT_WAIT_MS = 1e4;
|
|
44516
|
+
var PREVIOUS_EXIT_POLL_MS = 100;
|
|
44517
|
+
function launchdPid(deps, target) {
|
|
44518
|
+
const r = deps.run("launchctl", ["print", target]);
|
|
44519
|
+
if (r.status !== 0) return void 0;
|
|
44520
|
+
const m = /^\s*pid = (\d+)\s*$/m.exec(r.stdout);
|
|
44521
|
+
const pid = m ? Number(m[1]) : NaN;
|
|
44522
|
+
return Number.isInteger(pid) && pid > 0 ? pid : void 0;
|
|
44523
|
+
}
|
|
44524
|
+
function waitForPreviousExit(deps, pid) {
|
|
44525
|
+
const deadline = deps.now() + PREVIOUS_EXIT_WAIT_MS;
|
|
44526
|
+
while (deps.pidAlive(pid)) {
|
|
44527
|
+
if (deps.now() >= deadline) {
|
|
44528
|
+
throw new Error(
|
|
44529
|
+
`the previous bridge (pid ${pid}) did not exit within ${PREVIOUS_EXIT_WAIT_MS / 1e3} s of launchctl bootout, so a new copy would find the instance lock held and stay down. Wait for that process to exit (check with: ps -p ${pid}), then run install again.`
|
|
44530
|
+
);
|
|
44531
|
+
}
|
|
44532
|
+
deps.sleepMs(PREVIOUS_EXIT_POLL_MS);
|
|
44533
|
+
}
|
|
44534
|
+
}
|
|
44281
44535
|
function tokenHint(input) {
|
|
44282
44536
|
return `${dirname8(input.credentialPath)}/bridge-token`;
|
|
44283
44537
|
}
|
|
@@ -44290,7 +44544,10 @@ function installService(input, deps) {
|
|
|
44290
44544
|
deps.mkdirp(dirname8(cfg.plistPath));
|
|
44291
44545
|
deps.mkdirp(dirname8(cfg.logPath));
|
|
44292
44546
|
deps.writeFile(cfg.plistPath, renderLaunchdPlist(cfg));
|
|
44293
|
-
|
|
44547
|
+
const target = `gui/${cfg.uid}/${cfg.label}`;
|
|
44548
|
+
const previousPid = launchdPid(deps, target);
|
|
44549
|
+
deps.run("launchctl", ["bootout", target]);
|
|
44550
|
+
if (previousPid !== void 0) waitForPreviousExit(deps, previousPid);
|
|
44294
44551
|
requireOk(deps.run("launchctl", ["bootstrap", `gui/${cfg.uid}`, cfg.plistPath]), "launchctl bootstrap");
|
|
44295
44552
|
deps.run("launchctl", ["enable", `gui/${cfg.uid}/${cfg.label}`]);
|
|
44296
44553
|
requireOk(deps.run("launchctl", ["kickstart", "-k", `gui/${cfg.uid}/${cfg.label}`]), "launchctl kickstart");
|
|
@@ -44301,7 +44558,8 @@ function installService(input, deps) {
|
|
|
44301
44558
|
deps.writeFile(cfg.unitPath, renderSystemdUnit(cfg));
|
|
44302
44559
|
deps.run("systemctl", ["--user", "daemon-reload"]);
|
|
44303
44560
|
deps.run("loginctl", ["enable-linger", deps.user]);
|
|
44304
|
-
requireOk(deps.run("systemctl", ["--user", "enable",
|
|
44561
|
+
requireOk(deps.run("systemctl", ["--user", "enable", cfg.unitName]), "systemctl --user enable");
|
|
44562
|
+
requireOk(deps.run("systemctl", ["--user", "restart", cfg.unitName]), "systemctl --user restart");
|
|
44305
44563
|
return { label: cfg.unitName, filePath: cfg.unitPath, logHint: `journalctl --user -u ${cfg.unitName} -f`, tokenHint: tokenHint(input) };
|
|
44306
44564
|
}
|
|
44307
44565
|
throw new ServiceUnsupportedError(`Service install isn't supported on ${deps.platform} yet. Run the agent in a terminal, or use a process manager.`);
|
|
@@ -45050,7 +45308,7 @@ function renderStatusTable(rows) {
|
|
|
45050
45308
|
import {
|
|
45051
45309
|
chmodSync as chmodSync5,
|
|
45052
45310
|
mkdirSync as mkdirSync6,
|
|
45053
|
-
readFileSync as
|
|
45311
|
+
readFileSync as readFileSync13,
|
|
45054
45312
|
renameSync as renameSync4,
|
|
45055
45313
|
unlinkSync as unlinkSync2,
|
|
45056
45314
|
writeFileSync as writeFileSync3
|
|
@@ -45062,7 +45320,7 @@ var REAL_FS2 = {
|
|
|
45062
45320
|
chmod: (p, mode) => chmodSync5(p, mode),
|
|
45063
45321
|
writeFile: (p, data, mode) => writeFileSync3(p, data, { mode }),
|
|
45064
45322
|
rename: (a, b) => renameSync4(a, b),
|
|
45065
|
-
readFile: (p) =>
|
|
45323
|
+
readFile: (p) => readFileSync13(p, "utf8"),
|
|
45066
45324
|
unlink: (p) => unlinkSync2(p)
|
|
45067
45325
|
};
|
|
45068
45326
|
function securecomsDir(home) {
|
|
@@ -45111,7 +45369,7 @@ function removeConnectionFileIfOwned(home, identityId, instanceId, deps = REAL_F
|
|
|
45111
45369
|
|
|
45112
45370
|
// src/service/instance-lock.ts
|
|
45113
45371
|
import { execFileSync } from "node:child_process";
|
|
45114
|
-
import { closeSync as closeSync4, fsyncSync, mkdirSync as mkdirSync7, openSync as openSync4, readFileSync as
|
|
45372
|
+
import { closeSync as closeSync4, fsyncSync, mkdirSync as mkdirSync7, openSync as openSync4, readFileSync as readFileSync14, statSync as statSync6, unlinkSync as unlinkSync3, utimesSync, writeSync as writeSync2 } from "node:fs";
|
|
45115
45373
|
import { hostname, uptime as uptime2 } from "node:os";
|
|
45116
45374
|
import { dirname as dirname11 } from "node:path";
|
|
45117
45375
|
var LOCK_PARTIAL_WRITE_GRACE_MS = 1e4;
|
|
@@ -45125,7 +45383,7 @@ function errno(error) {
|
|
|
45125
45383
|
}
|
|
45126
45384
|
function readTrimmed(path) {
|
|
45127
45385
|
try {
|
|
45128
|
-
const v =
|
|
45386
|
+
const v = readFileSync14(path, "utf8").trim();
|
|
45129
45387
|
return v === "" ? void 0 : v;
|
|
45130
45388
|
} catch {
|
|
45131
45389
|
return void 0;
|
|
@@ -45240,7 +45498,7 @@ var InstanceLock = class {
|
|
|
45240
45498
|
if (!this.held) return false;
|
|
45241
45499
|
let current;
|
|
45242
45500
|
try {
|
|
45243
|
-
current =
|
|
45501
|
+
current = readFileSync14(this.path, "utf8");
|
|
45244
45502
|
} catch {
|
|
45245
45503
|
current = void 0;
|
|
45246
45504
|
}
|
|
@@ -45258,7 +45516,7 @@ var InstanceLock = class {
|
|
|
45258
45516
|
let ageMs;
|
|
45259
45517
|
this.env.beforeLockRead?.();
|
|
45260
45518
|
try {
|
|
45261
|
-
raw =
|
|
45519
|
+
raw = readFileSync14(this.path, "utf8");
|
|
45262
45520
|
ageMs = Date.now() - statSync6(this.path).mtimeMs;
|
|
45263
45521
|
} catch (error) {
|
|
45264
45522
|
if (errno(error) === "ENOENT") {
|
|
@@ -45299,7 +45557,7 @@ var InstanceLock = class {
|
|
|
45299
45557
|
const written = this.written;
|
|
45300
45558
|
this.drop();
|
|
45301
45559
|
try {
|
|
45302
|
-
if (written !== void 0 &&
|
|
45560
|
+
if (written !== void 0 && readFileSync14(this.path, "utf8") === written) unlinkSync3(this.path);
|
|
45303
45561
|
} catch {
|
|
45304
45562
|
}
|
|
45305
45563
|
}
|
|
@@ -45313,7 +45571,7 @@ var InstanceLock = class {
|
|
|
45313
45571
|
took() {
|
|
45314
45572
|
this.held = true;
|
|
45315
45573
|
try {
|
|
45316
|
-
this.written =
|
|
45574
|
+
this.written = readFileSync14(this.path, "utf8");
|
|
45317
45575
|
} catch {
|
|
45318
45576
|
this.written = void 0;
|
|
45319
45577
|
}
|
|
@@ -45395,7 +45653,7 @@ var InstanceLock = class {
|
|
|
45395
45653
|
this.env.beforeTakeoverReread?.();
|
|
45396
45654
|
let current;
|
|
45397
45655
|
try {
|
|
45398
|
-
current =
|
|
45656
|
+
current = readFileSync14(this.path, "utf8");
|
|
45399
45657
|
} catch {
|
|
45400
45658
|
current = void 0;
|
|
45401
45659
|
}
|
|
@@ -45409,7 +45667,7 @@ var InstanceLock = class {
|
|
|
45409
45667
|
if (created !== "created") {
|
|
45410
45668
|
if (created === "failed") return "failed";
|
|
45411
45669
|
try {
|
|
45412
|
-
this.holderPid = parseBody(
|
|
45670
|
+
this.holderPid = parseBody(readFileSync14(this.path, "utf8"))?.pid;
|
|
45413
45671
|
} catch {
|
|
45414
45672
|
}
|
|
45415
45673
|
return "held";
|
|
@@ -45435,7 +45693,7 @@ import {
|
|
|
45435
45693
|
fsyncSync as fsyncSync2,
|
|
45436
45694
|
lstatSync as lstatSync8,
|
|
45437
45695
|
openSync as openSync5,
|
|
45438
|
-
readFileSync as
|
|
45696
|
+
readFileSync as readFileSync15,
|
|
45439
45697
|
realpathSync as realpathSync2,
|
|
45440
45698
|
renameSync as renameSync5,
|
|
45441
45699
|
unlinkSync as unlinkSync4,
|
|
@@ -45472,7 +45730,7 @@ function isSymlink(path) {
|
|
|
45472
45730
|
}
|
|
45473
45731
|
}
|
|
45474
45732
|
function readJson(path) {
|
|
45475
|
-
return JSON.parse(
|
|
45733
|
+
return JSON.parse(readFileSync15(path, "utf8"));
|
|
45476
45734
|
}
|
|
45477
45735
|
function publicKeyOf(v) {
|
|
45478
45736
|
const r = parsePinObject(v);
|
|
@@ -45617,6 +45875,20 @@ function runPinCli(argv, io) {
|
|
|
45617
45875
|
return 1;
|
|
45618
45876
|
}
|
|
45619
45877
|
|
|
45878
|
+
// src/service/credential-arg.ts
|
|
45879
|
+
function readCredentialArg(argv, env) {
|
|
45880
|
+
const i = argv.indexOf("--credential");
|
|
45881
|
+
if (i >= 0) {
|
|
45882
|
+
const v = argv[i + 1];
|
|
45883
|
+
return v ? { kind: "path", path: v } : { kind: "empty_flag" };
|
|
45884
|
+
}
|
|
45885
|
+
const fromEnv = env.SECURECOMS_CREDENTIAL;
|
|
45886
|
+
return fromEnv ? { kind: "path", path: fromEnv } : { kind: "absent" };
|
|
45887
|
+
}
|
|
45888
|
+
function noCredentialLine(verb) {
|
|
45889
|
+
return verb === "run" ? "No key file was given. If you used the copied run command, no key file for this agent was found in your Downloads folder." : "No key file was found in your Downloads folder. Download the key file again from SecureComs, or add --credential <path>.";
|
|
45890
|
+
}
|
|
45891
|
+
|
|
45620
45892
|
// src/bin.ts
|
|
45621
45893
|
function nodeMajorOk(version) {
|
|
45622
45894
|
return Number(version.split(".")[0]) >= 24;
|
|
@@ -45653,15 +45925,22 @@ function realServiceDeps() {
|
|
|
45653
45925
|
removeFile: (p) => rmSync8(p, { force: true }),
|
|
45654
45926
|
mkdirp: (p) => mkdirSync8(p, { recursive: true }),
|
|
45655
45927
|
fileExists: (p) => existsSync7(p),
|
|
45656
|
-
user: userInfo().username
|
|
45928
|
+
user: userInfo().username,
|
|
45929
|
+
pidAlive: pidAliveByKill,
|
|
45930
|
+
// installService is synchronous; Atomics.wait is a real blocking sleep.
|
|
45931
|
+
sleepMs: (ms) => {
|
|
45932
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
45933
|
+
},
|
|
45934
|
+
now: () => Date.now()
|
|
45657
45935
|
};
|
|
45658
45936
|
}
|
|
45659
45937
|
function chooseInstallExecPath(runningExecPath, override, deps) {
|
|
45660
45938
|
if (override) return override;
|
|
45661
45939
|
const r = resolveStableNodePath(runningExecPath, deps);
|
|
45662
45940
|
if (!r.stable) {
|
|
45941
|
+
const list = STABLE_LOCATIONS.slice(0, -1).join(", ") + " or " + STABLE_LOCATIONS[STABLE_LOCATIONS.length - 1];
|
|
45663
45942
|
throw new Error(
|
|
45664
|
-
"
|
|
45943
|
+
"The agent's background service needs Node " + REQUIRED_NODE_MAJOR + " or newer at a fixed location (" + list + "), and none was found. Node from nvm, fnm and similar tools moves when it is upgraded, so it is not used. Install Node " + REQUIRED_NODE_MAJOR + " from https://nodejs.org, then run the install command again."
|
|
45665
45944
|
);
|
|
45666
45945
|
}
|
|
45667
45946
|
return r.path;
|
|
@@ -45695,7 +45974,7 @@ function realResolveDeps() {
|
|
|
45695
45974
|
}
|
|
45696
45975
|
function realPrepDeps() {
|
|
45697
45976
|
return {
|
|
45698
|
-
readFile: (p) =>
|
|
45977
|
+
readFile: (p) => readFileSync17(p, "utf8"),
|
|
45699
45978
|
writeFileMode: (p, data, mode) => writeFileSync5(p, data, { mode }),
|
|
45700
45979
|
mkdirp: (p, mode) => {
|
|
45701
45980
|
mkdirSync8(p, { recursive: true, mode: mode ?? 493 });
|
|
@@ -45748,7 +46027,7 @@ function realReadFleet() {
|
|
|
45748
46027
|
return [];
|
|
45749
46028
|
}
|
|
45750
46029
|
},
|
|
45751
|
-
readFile: (p) =>
|
|
46030
|
+
readFile: (p) => readFileSync17(p, "utf8")
|
|
45752
46031
|
});
|
|
45753
46032
|
}
|
|
45754
46033
|
function runStatus() {
|
|
@@ -45773,7 +46052,7 @@ function realCopyConnector(srcDir, dstDir, harness) {
|
|
|
45773
46052
|
}
|
|
45774
46053
|
if (name.includes(".test.")) continue;
|
|
45775
46054
|
if (!allowedExt.some((ext) => name.endsWith(ext))) continue;
|
|
45776
|
-
writeFileSync5(join25(dstDir, name),
|
|
46055
|
+
writeFileSync5(join25(dstDir, name), readFileSync17(srcPath));
|
|
45777
46056
|
}
|
|
45778
46057
|
}
|
|
45779
46058
|
function realWriteBinding(dstDir, identityId) {
|
|
@@ -45782,7 +46061,7 @@ function realWriteBinding(dstDir, identityId) {
|
|
|
45782
46061
|
}
|
|
45783
46062
|
function realReadBinding(dstDir) {
|
|
45784
46063
|
try {
|
|
45785
|
-
const parsed = JSON.parse(
|
|
46064
|
+
const parsed = JSON.parse(readFileSync17(join25(dstDir, "securecoms.binding.json"), "utf8"));
|
|
45786
46065
|
return parsed.identity_id ?? null;
|
|
45787
46066
|
} catch {
|
|
45788
46067
|
return null;
|
|
@@ -45800,7 +46079,7 @@ function realStatDir(p) {
|
|
|
45800
46079
|
}
|
|
45801
46080
|
function realReadHermesConfig(p) {
|
|
45802
46081
|
try {
|
|
45803
|
-
return
|
|
46082
|
+
return readFileSync17(p, "utf8");
|
|
45804
46083
|
} catch {
|
|
45805
46084
|
return null;
|
|
45806
46085
|
}
|
|
@@ -45841,9 +46120,17 @@ function runWireVerb(verb) {
|
|
|
45841
46120
|
process.exit(verb === "wire" ? runWire(realWireDeps()) : runUnwire(realWireDeps()));
|
|
45842
46121
|
}
|
|
45843
46122
|
async function runServiceVerb(verb) {
|
|
45844
|
-
|
|
46123
|
+
let credentialPath;
|
|
46124
|
+
if (verb === "install") {
|
|
46125
|
+
const cred = readCredentialArg(process.argv, process.env);
|
|
46126
|
+
credentialPath = cred.kind === "path" ? cred.path : void 0;
|
|
46127
|
+
} else {
|
|
46128
|
+
credentialPath = argOrEnv("--credential", "SECURECOMS_CREDENTIAL");
|
|
46129
|
+
}
|
|
45845
46130
|
const label = argOrEnv("--label", "SECURECOMS_LABEL");
|
|
45846
46131
|
if (verb === "install" ? !credentialPath : !credentialPath && !label) {
|
|
46132
|
+
if (verb === "install") process.stderr.write(`${noCredentialLine("install")}
|
|
46133
|
+
`);
|
|
45847
46134
|
const execPathUsage = verb === "install" ? " [--exec-path <path>]" : "";
|
|
45848
46135
|
process.stderr.write(`usage: agent-bridge ${verb} --credential <path> [--label <name>] [--store <path>] [--port N] [--harness openclaw|hermes|universal]${execPathUsage}
|
|
45849
46136
|
`);
|
|
@@ -45966,6 +46253,26 @@ var HELD_POLL_TOTAL_MS = 1e4;
|
|
|
45966
46253
|
var HELD_POLL_INTERVAL_MS = 500;
|
|
45967
46254
|
var HELD_NO_PID_REACQUIRES = 3;
|
|
45968
46255
|
var SHUTDOWN_HARD_EXIT_MS = 8e3;
|
|
46256
|
+
function configErrorLine(reason, o) {
|
|
46257
|
+
const at = o.isTTY ? ` at ${o.path}` : "";
|
|
46258
|
+
switch (reason) {
|
|
46259
|
+
case "credential_not_found":
|
|
46260
|
+
return `Can't find the key file${at}. Check the path after --credential.`;
|
|
46261
|
+
case "credential_unreadable":
|
|
46262
|
+
return `Can't open the key file${at}. Check that it exists and that your user can read it.`;
|
|
46263
|
+
case "credential_not_json":
|
|
46264
|
+
case "identity_id_invalid":
|
|
46265
|
+
return `The key file${at} is damaged. Get a new key file from the agent's page in SecureComs.`;
|
|
46266
|
+
case "credential_missing_field":
|
|
46267
|
+
return `The key file${at} is incomplete (no ${o.field}). Get a new key file from the agent's page in SecureComs.`;
|
|
46268
|
+
case "credential_voice_invalid":
|
|
46269
|
+
return o.source === "env" ? "The voice settings in this computer's environment are not valid." : `The voice settings in the key file${at} are not valid.`;
|
|
46270
|
+
case "bind_refused":
|
|
46271
|
+
return "The Connector only accepts connections from this computer unless TLS is set. Remove --bind, or add --tls-cert and --tls-key.";
|
|
46272
|
+
case "unknown":
|
|
46273
|
+
return void 0;
|
|
46274
|
+
}
|
|
46275
|
+
}
|
|
45969
46276
|
function realRunModeDeps(credentialPath, storePath) {
|
|
45970
46277
|
return {
|
|
45971
46278
|
credentialPath,
|
|
@@ -45996,22 +46303,56 @@ function realRunModeDeps(credentialPath, storePath) {
|
|
|
45996
46303
|
process.stderr.write(`${line}
|
|
45997
46304
|
`);
|
|
45998
46305
|
},
|
|
46306
|
+
isTTY: process.stderr.isTTY === true,
|
|
45999
46307
|
log: writeServiceLog
|
|
46000
46308
|
};
|
|
46001
46309
|
}
|
|
46002
46310
|
async function runMode(deps) {
|
|
46003
46311
|
const { credentialPath, storePath, log } = deps;
|
|
46312
|
+
const refuse2 = (reason, extra, e) => {
|
|
46313
|
+
const fields = {
|
|
46314
|
+
reason: ServiceLogEnum.of(reason, [
|
|
46315
|
+
"credential_not_found",
|
|
46316
|
+
"credential_unreadable",
|
|
46317
|
+
"credential_not_json",
|
|
46318
|
+
"credential_missing_field",
|
|
46319
|
+
"credential_voice_invalid",
|
|
46320
|
+
"identity_id_invalid",
|
|
46321
|
+
"bind_refused",
|
|
46322
|
+
"unknown"
|
|
46323
|
+
])
|
|
46324
|
+
};
|
|
46325
|
+
if (extra.field) {
|
|
46326
|
+
fields.field = ServiceLogEnum.of(extra.field, [
|
|
46327
|
+
"api_key",
|
|
46328
|
+
"identity_id",
|
|
46329
|
+
"device_id",
|
|
46330
|
+
"signing_private_key",
|
|
46331
|
+
"enclave_id",
|
|
46332
|
+
"api_base"
|
|
46333
|
+
]);
|
|
46334
|
+
}
|
|
46335
|
+
log("error", "startup.config_invalid", fields, e);
|
|
46336
|
+
const line = configErrorLine(reason, { path: credentialPath, isTTY: deps.isTTY, ...extra });
|
|
46337
|
+
if (line !== void 0) deps.print(line);
|
|
46338
|
+
};
|
|
46004
46339
|
let cfg, daemon;
|
|
46005
46340
|
try {
|
|
46006
46341
|
cfg = loadConfig({ credentialPath, storePath });
|
|
46007
46342
|
daemon = parseDaemonArgs(deps.argv, deps.env);
|
|
46343
|
+
} catch (e) {
|
|
46344
|
+
if (e instanceof ConfigError) refuse2(e.reason, { field: e.field, source: e.source }, e);
|
|
46345
|
+
else refuse2("unknown", {}, e);
|
|
46346
|
+
return deps.exit(1);
|
|
46347
|
+
}
|
|
46348
|
+
try {
|
|
46008
46349
|
validateBind(daemon.bind, daemon.tls);
|
|
46009
46350
|
} catch (e) {
|
|
46010
|
-
|
|
46351
|
+
refuse2("bind_refused", {}, e);
|
|
46011
46352
|
return deps.exit(1);
|
|
46012
46353
|
}
|
|
46013
46354
|
if (!CANONICAL_UUID2.test(cfg.identityId)) {
|
|
46014
|
-
|
|
46355
|
+
refuse2("identity_id_invalid", { field: "identity_id" });
|
|
46015
46356
|
return deps.exit(1);
|
|
46016
46357
|
}
|
|
46017
46358
|
const lockDir = join25(deps.home, ".securecoms", "locks");
|
|
@@ -46050,7 +46391,9 @@ async function runMode(deps) {
|
|
|
46050
46391
|
lock_path: ServiceLogEnum.of("home_securecoms_locks_identity", ["home_securecoms_locks_identity"]),
|
|
46051
46392
|
remedy: ServiceLogEnum.of("delete_lock_if_no_other_host", ["delete_lock_if_no_other_host"])
|
|
46052
46393
|
});
|
|
46053
|
-
deps.print(
|
|
46394
|
+
deps.print(
|
|
46395
|
+
deps.isTTY ? `This agent's lock file was written under a different computer name: ${lockPath}. If no other computer runs this agent, delete that file and start the agent again.` : "This agent's lock file was written under a different computer name. If no other computer runs this agent, delete that file and start the agent again."
|
|
46396
|
+
);
|
|
46054
46397
|
return deps.exit(1);
|
|
46055
46398
|
case "unreadable":
|
|
46056
46399
|
log("error", "startup.instance_lock_unreadable", {}, lock.lastError);
|
|
@@ -46126,7 +46469,7 @@ async function runMode(deps) {
|
|
|
46126
46469
|
served = await deps.serve(agent, {
|
|
46127
46470
|
token,
|
|
46128
46471
|
allowOrigins: daemon.allowOrigins,
|
|
46129
|
-
readTls: () => daemon.tls ? { cert:
|
|
46472
|
+
readTls: () => daemon.tls ? { cert: readFileSync17(daemon.tlsCert, "utf8"), key: readFileSync17(daemon.tlsKey, "utf8") } : void 0
|
|
46130
46473
|
});
|
|
46131
46474
|
} catch (e) {
|
|
46132
46475
|
if (!(e instanceof SessionSupersededError)) throw e;
|
|
@@ -46171,6 +46514,10 @@ async function runMode(deps) {
|
|
|
46171
46514
|
}
|
|
46172
46515
|
});
|
|
46173
46516
|
}
|
|
46517
|
+
function printNoCredentialRunLine() {
|
|
46518
|
+
process.stderr.write(`${noCredentialLine("run")}
|
|
46519
|
+
`);
|
|
46520
|
+
}
|
|
46174
46521
|
async function main() {
|
|
46175
46522
|
if (!nodeMajorOk(process.versions.node)) {
|
|
46176
46523
|
writeServiceLog("error", "startup.node_unsupported");
|
|
@@ -46197,11 +46544,13 @@ async function main() {
|
|
|
46197
46544
|
await runServiceVerb(verb);
|
|
46198
46545
|
return;
|
|
46199
46546
|
}
|
|
46200
|
-
const
|
|
46201
|
-
if (
|
|
46547
|
+
const cred = readCredentialArg(process.argv, process.env);
|
|
46548
|
+
if (cred.kind !== "path") {
|
|
46549
|
+
printNoCredentialRunLine();
|
|
46202
46550
|
writeServiceLog("error", "startup.credential_required.use_--credential");
|
|
46203
46551
|
process.exit(2);
|
|
46204
46552
|
}
|
|
46553
|
+
const credentialPath = cred.path;
|
|
46205
46554
|
const storePath = argOrEnv("--store", "SECURECOMS_STORE");
|
|
46206
46555
|
await runMode(realRunModeDeps(credentialPath, storePath));
|
|
46207
46556
|
}
|
|
@@ -46219,6 +46568,7 @@ export {
|
|
|
46219
46568
|
HELD_POLL_TOTAL_MS,
|
|
46220
46569
|
SHUTDOWN_HARD_EXIT_MS,
|
|
46221
46570
|
chooseInstallExecPath,
|
|
46571
|
+
configErrorLine,
|
|
46222
46572
|
formatServiceError,
|
|
46223
46573
|
nodeMajorOk,
|
|
46224
46574
|
parseVerb,
|