@muhaven/mcp 0.1.2 → 0.1.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/CHANGELOG.md +71 -1
- package/dist/broker.cjs +138 -22
- package/dist/broker.d.cts +21 -1
- package/dist/broker.d.ts +21 -1
- package/dist/broker.js +138 -23
- package/dist/index.cjs +124 -20
- package/dist/index.d.cts +143 -13
- package/dist/index.d.ts +143 -13
- package/dist/index.js +120 -21
- package/manifest.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,75 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.3] — 2026-05-16
|
|
11
|
+
|
|
12
|
+
Q2 fix bundle from the post-§4 queue closing four findings from §3e⁶
|
|
13
|
+
(broker-session-key-required-for-reads, broker-env-divergence,
|
|
14
|
+
mcp-serverinfo-version-stale) and unblocking the openclaw-skill ClawScan
|
|
15
|
+
fix (the `noExternal: ['@muhaven/mcp']` inline bundle requires this
|
|
16
|
+
version on npm before the skill can be republished).
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- **Read-only daemon posture**: the broker daemon now boots WITHOUT
|
|
21
|
+
`MUHAVEN_BROKER_SESSION_KEY`. In that mode the daemon still serves
|
|
22
|
+
`hello` + the JWT verbs (so `muhaven.read.*` tools work end-to-end via
|
|
23
|
+
the standalone `@muhaven/mcp` install), but any `sign_hash` request
|
|
24
|
+
returns the new `session_key_unavailable` broker error so write paths
|
|
25
|
+
fail with a clear remediation message instead of the daemon dying at
|
|
26
|
+
startup. Closes §3e⁶ F-broker-session-key-required-for-reads.
|
|
27
|
+
- **`muhaven-broker login --from-daemon` flag**: resolves backend +
|
|
28
|
+
dashboard URLs from the running daemon's `hello.effectiveConfig`
|
|
29
|
+
rather than the login CLI's env. Solves the daemon-vs-CLI env-divergence
|
|
30
|
+
problem when the two processes inherit different shell environments
|
|
31
|
+
(e.g. the daemon was launched by systemd/launchd, the CLI by ssh).
|
|
32
|
+
Mutually exclusive with explicit `--backend-base-url` /
|
|
33
|
+
`--dashboard-base-url`. Closes §3e⁶ F-broker-env-divergence.
|
|
34
|
+
- **`muhaven-broker doctor` surfaces the daemon's effective config**
|
|
35
|
+
and read-only-posture status — the operator can verify which backend
|
|
36
|
+
URL is actually in play before driving a login.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **Broker protocol bumped 0.2.0 → 0.3.0** (additive — pre-0.3.0 clients
|
|
41
|
+
remain compatible):
|
|
42
|
+
- `hello.hasSessionKey` (optional `boolean`) — absence implies `true`
|
|
43
|
+
for back-compat.
|
|
44
|
+
- `hello.effectiveConfig` (optional `{ backendBaseUrl, dashboardBaseUrl }`).
|
|
45
|
+
- New `session_key_unavailable` broker error code.
|
|
46
|
+
- **`serverInfo.version`** in the MCP server's `initialize` response is
|
|
47
|
+
now build-time injected from `package.json#version` (tsup `define` on
|
|
48
|
+
`__SERVER_VERSION__`) rather than the previously hardcoded `'0.1.0'`
|
|
49
|
+
string in `src/server.ts`. Closes §3e⁶ F-mcp-serverinfo-version-stale.
|
|
50
|
+
|
|
51
|
+
### Tests
|
|
52
|
+
|
|
53
|
+
- 134 vitest pass (up from 101 in 0.1.2). Net +33 cases:
|
|
54
|
+
- **+6** `config.test.ts` — `loadBrokerConfig` lazy-validation
|
|
55
|
+
(no key, empty string, valid key, malformed key) + env-driven backend
|
|
56
|
+
+ dashboard URL surface.
|
|
57
|
+
- **+5** `daemon-handler.test.ts` — 0.3.0 protocol: `hello` surfaces
|
|
58
|
+
`hasSessionKey` + `effectiveConfig` from options, defaults `true`
|
|
59
|
+
when omitted, reflects `false` when set; `sign_hash` with
|
|
60
|
+
`NullSigner` returns `session_key_unavailable`; re-throws non-Missing
|
|
61
|
+
errors verbatim.
|
|
62
|
+
- **+9** `cli-parse-login-flags.test.ts` — flag parser unit cases
|
|
63
|
+
incl. `--from-daemon` mutual-exclusion guard.
|
|
64
|
+
- **+8** `session-key-required.test.ts` — `signEnvelope` probe of
|
|
65
|
+
`hello.hasSessionKey`; short-circuit returns `SESSION_KEY_REQUIRED`
|
|
66
|
+
for buy + claim with mint-URL pointing at `dashboardBaseUrl`;
|
|
67
|
+
safety-net mapping of inner `session_key_unavailable`;
|
|
68
|
+
probe-cache reuse; concurrent-call coalescing (one hello round-trip
|
|
69
|
+
for N callers); retry-after-rejection (eager cache clear); trailing-slash
|
|
70
|
+
mintUrl strip.
|
|
71
|
+
- **+2** `server-version.test.ts` — runtime fallback returns
|
|
72
|
+
`package.json#version`; matches `manifest.json#version`.
|
|
73
|
+
- **+2** `build-artifacts.test.ts` — hostname-migration guard + new
|
|
74
|
+
`__SERVER_VERSION__` literal grep in bundled dist.
|
|
75
|
+
- **+1** `daemon-lifecycle.test.ts` — read-only-posture boot test
|
|
76
|
+
replaces the prior "exits on missing key" assertion; added
|
|
77
|
+
"exits on a malformed session key" as the second negative-path test.
|
|
78
|
+
|
|
10
79
|
## [0.1.2] — 2026-05-11
|
|
11
80
|
|
|
12
81
|
Re-roll of the `0.1.1` workflow-validation cut. `0.1.1` never reached npm:
|
|
@@ -181,7 +250,8 @@ Workstream H)
|
|
|
181
250
|
muhaven-mcp-0.1.0.tgz
|
|
182
251
|
```
|
|
183
252
|
|
|
184
|
-
[Unreleased]: https://github.com/hasToDev/muhaven/compare/mcp-v0.1.
|
|
253
|
+
[Unreleased]: https://github.com/hasToDev/muhaven/compare/mcp-v0.1.3...HEAD
|
|
254
|
+
[0.1.3]: https://github.com/hasToDev/muhaven/releases/tag/mcp-v0.1.3
|
|
185
255
|
[0.1.2]: https://github.com/hasToDev/muhaven/releases/tag/mcp-v0.1.2
|
|
186
256
|
[0.1.1]: https://github.com/hasToDev/muhaven/releases/tag/mcp-v0.1.1
|
|
187
257
|
[0.1.0]: https://github.com/hasToDev/muhaven/releases/tag/mcp-v0.1.0
|
package/dist/broker.cjs
CHANGED
|
@@ -73,23 +73,26 @@ function loadMcpConfig(env = process.env) {
|
|
|
73
73
|
}
|
|
74
74
|
var PRIVKEY_HEX_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
75
75
|
function loadBrokerConfig(env = process.env) {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
throw new Error("MUHAVEN_BROKER_SESSION_KEY must be a 0x-prefixed 32-byte hex string");
|
|
76
|
+
const sessionKeyHexRaw = env.MUHAVEN_BROKER_SESSION_KEY;
|
|
77
|
+
let sessionKeyHex;
|
|
78
|
+
if (sessionKeyHexRaw && sessionKeyHexRaw.length > 0) {
|
|
79
|
+
if (!PRIVKEY_HEX_RE.test(sessionKeyHexRaw)) {
|
|
80
|
+
throw new Error("MUHAVEN_BROKER_SESSION_KEY must be a 0x-prefixed 32-byte hex string");
|
|
81
|
+
}
|
|
82
|
+
sessionKeyHex = sessionKeyHexRaw;
|
|
84
83
|
}
|
|
85
84
|
const endpoint = env.MUHAVEN_BROKER_ENDPOINT ?? defaultBrokerEndpoint();
|
|
86
85
|
const maxRequestBytes = readEnvInt("MUHAVEN_BROKER_MAX_BYTES", DEFAULT_BROKER_MAX_BYTES, env);
|
|
87
86
|
const requestTimeoutMs = readEnvInt("MUHAVEN_BROKER_TIMEOUT_MS", DEFAULT_BROKER_TIMEOUT_MS, env);
|
|
87
|
+
const backendBaseUrl = trimTrailingSlash(env.MUHAVEN_BACKEND_URL ?? DEFAULT_BACKEND_URL);
|
|
88
|
+
const dashboardBaseUrl = trimTrailingSlash(env.MUHAVEN_DASHBOARD_URL ?? DEFAULT_DASHBOARD_URL);
|
|
88
89
|
return {
|
|
89
90
|
endpoint,
|
|
90
91
|
sessionKeyHex,
|
|
91
92
|
maxRequestBytes,
|
|
92
|
-
requestTimeoutMs
|
|
93
|
+
requestTimeoutMs,
|
|
94
|
+
backendBaseUrl,
|
|
95
|
+
dashboardBaseUrl
|
|
93
96
|
};
|
|
94
97
|
}
|
|
95
98
|
var BrokerClientError = class extends Error {
|
|
@@ -522,7 +525,7 @@ async function openKeystore(options = {}) {
|
|
|
522
525
|
}
|
|
523
526
|
|
|
524
527
|
// src/broker/protocol.ts
|
|
525
|
-
var BROKER_PROTOCOL_VERSION = "0.
|
|
528
|
+
var BROKER_PROTOCOL_VERSION = "0.3.0";
|
|
526
529
|
var HASH_HEX_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
527
530
|
var JWT_RE = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
|
|
528
531
|
function isHashHex(value) {
|
|
@@ -608,6 +611,21 @@ function parseBrokerRequest(line) {
|
|
|
608
611
|
function serializeResponse(res) {
|
|
609
612
|
return JSON.stringify(res) + "\n";
|
|
610
613
|
}
|
|
614
|
+
var MissingSessionKeyError = class extends Error {
|
|
615
|
+
constructor() {
|
|
616
|
+
super(
|
|
617
|
+
"session_key_unavailable: daemon booted in read-only posture (no MUHAVEN_BROKER_SESSION_KEY at env-load time). Mint a session key via the dashboard /agent/policy/transition flow, set MUHAVEN_BROKER_SESSION_KEY, and restart the daemon. (Note: `muhaven-broker login` mints a JWT, NOT a session key \u2014 do not loop on that command for this error.)"
|
|
618
|
+
);
|
|
619
|
+
this.name = "MissingSessionKeyError";
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
623
|
+
var NullSigner = class {
|
|
624
|
+
address = ZERO_ADDRESS;
|
|
625
|
+
async signHash(_hash) {
|
|
626
|
+
throw new MissingSessionKeyError();
|
|
627
|
+
}
|
|
628
|
+
};
|
|
611
629
|
var ViemSigner = class {
|
|
612
630
|
account;
|
|
613
631
|
constructor(privateKey) {
|
|
@@ -624,7 +642,7 @@ var ViemSigner = class {
|
|
|
624
642
|
// src/broker/daemon.ts
|
|
625
643
|
var noopLogger = (_e) => {
|
|
626
644
|
};
|
|
627
|
-
async function handleBrokerRequest(req, signer, keystore, nowSec = () => Math.floor(Date.now() / 1e3)) {
|
|
645
|
+
async function handleBrokerRequest(req, signer, keystore, nowSec = () => Math.floor(Date.now() / 1e3), options = {}) {
|
|
628
646
|
switch (req.type) {
|
|
629
647
|
case "hello": {
|
|
630
648
|
let hasJwt = false;
|
|
@@ -634,16 +652,26 @@ async function handleBrokerRequest(req, signer, keystore, nowSec = () => Math.fl
|
|
|
634
652
|
} catch {
|
|
635
653
|
hasJwt = false;
|
|
636
654
|
}
|
|
655
|
+
const hasSessionKey = options.hasSessionKey ?? true;
|
|
637
656
|
return {
|
|
638
657
|
type: "hello",
|
|
639
658
|
version: BROKER_PROTOCOL_VERSION,
|
|
640
659
|
sessionKeyAddress: signer.address,
|
|
641
|
-
hasJwt
|
|
660
|
+
hasJwt,
|
|
661
|
+
hasSessionKey,
|
|
662
|
+
...options.effectiveConfig ? { effectiveConfig: options.effectiveConfig } : {}
|
|
642
663
|
};
|
|
643
664
|
}
|
|
644
665
|
case "sign_hash": {
|
|
645
|
-
|
|
646
|
-
|
|
666
|
+
try {
|
|
667
|
+
const signature = await signer.signHash(req.hash);
|
|
668
|
+
return { type: "sign_hash", signature, signerAddress: signer.address };
|
|
669
|
+
} catch (err) {
|
|
670
|
+
if (err instanceof MissingSessionKeyError) {
|
|
671
|
+
return errorResponse("session_key_unavailable", err.message);
|
|
672
|
+
}
|
|
673
|
+
throw err;
|
|
674
|
+
}
|
|
647
675
|
}
|
|
648
676
|
case "store_jwt": {
|
|
649
677
|
try {
|
|
@@ -715,9 +743,24 @@ var BrokerDaemon = class {
|
|
|
715
743
|
log;
|
|
716
744
|
config;
|
|
717
745
|
keystore;
|
|
746
|
+
/**
|
|
747
|
+
* Whether a session-key private half is actually loaded. `false` =
|
|
748
|
+
* daemon booted in read-only posture (no `MUHAVEN_BROKER_SESSION_KEY`
|
|
749
|
+
* at env-load time) and uses a `NullSigner` whose `signHash` throws.
|
|
750
|
+
*/
|
|
751
|
+
hasSessionKey;
|
|
718
752
|
constructor(options) {
|
|
719
753
|
this.config = options.config;
|
|
720
|
-
|
|
754
|
+
if (options.signer) {
|
|
755
|
+
this.signer = options.signer;
|
|
756
|
+
this.hasSessionKey = true;
|
|
757
|
+
} else if (options.config.sessionKeyHex) {
|
|
758
|
+
this.signer = new ViemSigner(options.config.sessionKeyHex);
|
|
759
|
+
this.hasSessionKey = true;
|
|
760
|
+
} else {
|
|
761
|
+
this.signer = new NullSigner();
|
|
762
|
+
this.hasSessionKey = false;
|
|
763
|
+
}
|
|
721
764
|
this.keystore = options.keystore ?? null;
|
|
722
765
|
this.log = options.logger ?? noopLogger;
|
|
723
766
|
this.server = net.createServer((socket) => this.onConnection(socket));
|
|
@@ -755,6 +798,7 @@ var BrokerDaemon = class {
|
|
|
755
798
|
meta: {
|
|
756
799
|
endpoint: this.config.endpoint,
|
|
757
800
|
signer: this.signer.address,
|
|
801
|
+
hasSessionKey: this.hasSessionKey,
|
|
758
802
|
keystore: this.keystore.backend,
|
|
759
803
|
version: BROKER_PROTOCOL_VERSION
|
|
760
804
|
}
|
|
@@ -836,7 +880,19 @@ var BrokerDaemon = class {
|
|
|
836
880
|
return;
|
|
837
881
|
}
|
|
838
882
|
try {
|
|
839
|
-
const res = await handleBrokerRequest(
|
|
883
|
+
const res = await handleBrokerRequest(
|
|
884
|
+
parsed,
|
|
885
|
+
this.signer,
|
|
886
|
+
this.keystore,
|
|
887
|
+
void 0,
|
|
888
|
+
{
|
|
889
|
+
hasSessionKey: this.hasSessionKey,
|
|
890
|
+
effectiveConfig: {
|
|
891
|
+
backendBaseUrl: this.config.backendBaseUrl,
|
|
892
|
+
dashboardBaseUrl: this.config.dashboardBaseUrl
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
);
|
|
840
896
|
socket.end(serializeResponse(res));
|
|
841
897
|
} catch (err) {
|
|
842
898
|
this.log({
|
|
@@ -852,6 +908,15 @@ var BrokerDaemon = class {
|
|
|
852
908
|
};
|
|
853
909
|
async function runBrokerDaemonCli() {
|
|
854
910
|
const config = loadBrokerConfig();
|
|
911
|
+
if (!config.sessionKeyHex) {
|
|
912
|
+
process.stderr.write(
|
|
913
|
+
JSON.stringify({
|
|
914
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
915
|
+
level: "info",
|
|
916
|
+
msg: "broker booting in read-only posture (no MUHAVEN_BROKER_SESSION_KEY)"
|
|
917
|
+
}) + "\n"
|
|
918
|
+
);
|
|
919
|
+
}
|
|
855
920
|
const daemon = new BrokerDaemon({
|
|
856
921
|
config,
|
|
857
922
|
logger: (e) => {
|
|
@@ -904,9 +969,11 @@ function parseLoginFlags(argv) {
|
|
|
904
969
|
let brokerEndpoint;
|
|
905
970
|
let backendBaseUrl;
|
|
906
971
|
let dashboardBaseUrl;
|
|
972
|
+
let fromDaemon = false;
|
|
907
973
|
for (let i = 0; i < argv.length; i++) {
|
|
908
974
|
const a = argv[i];
|
|
909
975
|
if (a === "--no-launch-browser") noLaunchBrowser = true;
|
|
976
|
+
else if (a === "--from-daemon") fromDaemon = true;
|
|
910
977
|
else if (a === "--broker-endpoint" && i + 1 < argv.length) {
|
|
911
978
|
brokerEndpoint = argv[++i];
|
|
912
979
|
} else if (a === "--backend-base-url" && i + 1 < argv.length) {
|
|
@@ -917,7 +984,12 @@ function parseLoginFlags(argv) {
|
|
|
917
984
|
throw new Error(`unknown flag: ${a}`);
|
|
918
985
|
}
|
|
919
986
|
}
|
|
920
|
-
|
|
987
|
+
if (fromDaemon && (backendBaseUrl || dashboardBaseUrl)) {
|
|
988
|
+
throw new Error(
|
|
989
|
+
"--from-daemon is mutually exclusive with --backend-base-url / --dashboard-base-url"
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
return { noLaunchBrowser, brokerEndpoint, backendBaseUrl, dashboardBaseUrl, fromDaemon };
|
|
921
993
|
}
|
|
922
994
|
async function tryLaunchBrowser(url) {
|
|
923
995
|
return new Promise((resolve) => {
|
|
@@ -931,7 +1003,9 @@ async function runLogin(argv) {
|
|
|
931
1003
|
flags = parseLoginFlags(argv);
|
|
932
1004
|
} catch (err) {
|
|
933
1005
|
printErr(`error: ${err.message}`);
|
|
934
|
-
printErr(
|
|
1006
|
+
printErr(
|
|
1007
|
+
"usage: muhaven-broker login [--no-launch-browser] [--broker-endpoint PATH] [--from-daemon | (--backend-base-url URL --dashboard-base-url URL)]"
|
|
1008
|
+
);
|
|
935
1009
|
return 2;
|
|
936
1010
|
}
|
|
937
1011
|
const env = process.env;
|
|
@@ -945,8 +1019,9 @@ async function runLogin(argv) {
|
|
|
945
1019
|
endpoint: config.brokerEndpoint,
|
|
946
1020
|
timeoutMs: config.brokerTimeoutMs
|
|
947
1021
|
});
|
|
1022
|
+
let helloResult;
|
|
948
1023
|
try {
|
|
949
|
-
await broker.hello();
|
|
1024
|
+
helloResult = await broker.hello();
|
|
950
1025
|
} catch (err) {
|
|
951
1026
|
printErr(
|
|
952
1027
|
`cannot reach muhaven-broker daemon at ${config.brokerEndpoint}: ${err.message}`
|
|
@@ -954,9 +1029,42 @@ async function runLogin(argv) {
|
|
|
954
1029
|
printErr("hint: start the daemon first (`muhaven-broker` with no subcommand).");
|
|
955
1030
|
return 1;
|
|
956
1031
|
}
|
|
1032
|
+
let backendBaseUrl = config.backendBaseUrl;
|
|
1033
|
+
let dashboardBaseUrl = config.dashboardBaseUrl;
|
|
1034
|
+
if (flags.fromDaemon) {
|
|
1035
|
+
if (!helloResult.effectiveConfig) {
|
|
1036
|
+
printErr(
|
|
1037
|
+
"--from-daemon requested but broker did not return effectiveConfig (daemon is older than protocol 0.3.0). Upgrade the daemon (`@muhaven/mcp@0.1.3+`) or drop the flag."
|
|
1038
|
+
);
|
|
1039
|
+
return 1;
|
|
1040
|
+
}
|
|
1041
|
+
const daemonBackend = helloResult.effectiveConfig.backendBaseUrl;
|
|
1042
|
+
const daemonDashboard = helloResult.effectiveConfig.dashboardBaseUrl;
|
|
1043
|
+
if (!daemonBackend || !daemonDashboard) {
|
|
1044
|
+
printErr(
|
|
1045
|
+
"--from-daemon: daemon returned an empty backend/dashboard URL \u2014 refusing to proceed."
|
|
1046
|
+
);
|
|
1047
|
+
return 1;
|
|
1048
|
+
}
|
|
1049
|
+
if (daemonBackend !== config.backendBaseUrl) {
|
|
1050
|
+
print(
|
|
1051
|
+
`\u26A0 daemon backend (${daemonBackend}) differs from CLI env (${config.backendBaseUrl}). Using daemon's value per --from-daemon.`
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
if (daemonDashboard !== config.dashboardBaseUrl) {
|
|
1055
|
+
print(
|
|
1056
|
+
`\u26A0 daemon dashboard (${daemonDashboard}) differs from CLI env (${config.dashboardBaseUrl}). Using daemon's value per --from-daemon.`
|
|
1057
|
+
);
|
|
1058
|
+
}
|
|
1059
|
+
backendBaseUrl = daemonBackend;
|
|
1060
|
+
dashboardBaseUrl = daemonDashboard;
|
|
1061
|
+
print(`Using daemon's effective config:`);
|
|
1062
|
+
print(` backend: ${backendBaseUrl}`);
|
|
1063
|
+
print(` dashboard: ${dashboardBaseUrl}`);
|
|
1064
|
+
}
|
|
957
1065
|
const flow = new DeviceFlowClient({
|
|
958
|
-
backendBaseUrl
|
|
959
|
-
dashboardBaseUrl
|
|
1066
|
+
backendBaseUrl,
|
|
1067
|
+
dashboardBaseUrl,
|
|
960
1068
|
requesterMetadata: {
|
|
961
1069
|
processName: detectMcpHost(),
|
|
962
1070
|
hostname: os.hostname(),
|
|
@@ -1071,7 +1179,13 @@ async function runDoctor() {
|
|
|
1071
1179
|
});
|
|
1072
1180
|
try {
|
|
1073
1181
|
const h = await broker.hello();
|
|
1074
|
-
|
|
1182
|
+
const hasKey = h.hasSessionKey ?? true;
|
|
1183
|
+
const keyTag = hasKey ? `signer ${h.sessionKeyAddress}` : "NO SESSION KEY (read-only posture)";
|
|
1184
|
+
print(`Broker daemon : reachable (proto v${h.version}, ${keyTag}, hasJwt=${h.hasJwt})`);
|
|
1185
|
+
if (h.effectiveConfig) {
|
|
1186
|
+
print(`Daemon backend URL: ${h.effectiveConfig.backendBaseUrl}`);
|
|
1187
|
+
print(`Daemon dashboard : ${h.effectiveConfig.dashboardBaseUrl}`);
|
|
1188
|
+
}
|
|
1075
1189
|
return 0;
|
|
1076
1190
|
} catch (err) {
|
|
1077
1191
|
print(`Broker daemon : NOT reachable (${err.message})`);
|
|
@@ -1084,6 +1198,7 @@ function printUsage() {
|
|
|
1084
1198
|
print("");
|
|
1085
1199
|
print(" (no subcommand) Run the daemon (production mode)");
|
|
1086
1200
|
print(" login Acquire a JWT via the device-code flow + store in keystore");
|
|
1201
|
+
print(" [--from-daemon] resolves backend/dashboard URLs from the running daemon");
|
|
1087
1202
|
print(" logout Clear the JWT from the keystore");
|
|
1088
1203
|
print(" doctor Print environment + keystore + reachability report");
|
|
1089
1204
|
print(" -h, --help Show this help");
|
|
@@ -1111,6 +1226,7 @@ async function runCli(argv) {
|
|
|
1111
1226
|
}
|
|
1112
1227
|
}
|
|
1113
1228
|
|
|
1229
|
+
exports.parseLoginFlags = parseLoginFlags;
|
|
1114
1230
|
exports.runCli = runCli;
|
|
1115
1231
|
exports.runDoctor = runDoctor;
|
|
1116
1232
|
exports.runLogin = runLogin;
|
package/dist/broker.d.cts
CHANGED
|
@@ -8,9 +8,29 @@
|
|
|
8
8
|
* doctor → environment + keystore capability report
|
|
9
9
|
* --help, -h → usage
|
|
10
10
|
*/
|
|
11
|
+
interface LoginFlags {
|
|
12
|
+
noLaunchBrowser: boolean;
|
|
13
|
+
brokerEndpoint?: string;
|
|
14
|
+
backendBaseUrl?: string;
|
|
15
|
+
dashboardBaseUrl?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Resolve `backendBaseUrl` + `dashboardBaseUrl` from the running
|
|
18
|
+
* daemon's view (returned in `hello.effectiveConfig`) rather than the
|
|
19
|
+
* CLI's env. Solves the daemon-vs-CLI env-divergence problem when the
|
|
20
|
+
* CLI is launched from a different shell (ssh, IDE-spawned terminal)
|
|
21
|
+
* than the systemd / launchd-launched daemon. Closes §3e⁶
|
|
22
|
+
* F-broker-env-divergence.
|
|
23
|
+
*
|
|
24
|
+
* Mutually exclusive with explicit `--backend-base-url` /
|
|
25
|
+
* `--dashboard-base-url`; the CLI rejects the combination so the
|
|
26
|
+
* operator picks one source of truth.
|
|
27
|
+
*/
|
|
28
|
+
fromDaemon: boolean;
|
|
29
|
+
}
|
|
30
|
+
declare function parseLoginFlags(argv: readonly string[]): LoginFlags;
|
|
11
31
|
declare function runLogin(argv: readonly string[]): Promise<number>;
|
|
12
32
|
declare function runLogout(): Promise<number>;
|
|
13
33
|
declare function runDoctor(): Promise<number>;
|
|
14
34
|
declare function runCli(argv: readonly string[]): Promise<number>;
|
|
15
35
|
|
|
16
|
-
export { runCli, runDoctor, runLogin, runLogout };
|
|
36
|
+
export { parseLoginFlags, runCli, runDoctor, runLogin, runLogout };
|
package/dist/broker.d.ts
CHANGED
|
@@ -8,9 +8,29 @@
|
|
|
8
8
|
* doctor → environment + keystore capability report
|
|
9
9
|
* --help, -h → usage
|
|
10
10
|
*/
|
|
11
|
+
interface LoginFlags {
|
|
12
|
+
noLaunchBrowser: boolean;
|
|
13
|
+
brokerEndpoint?: string;
|
|
14
|
+
backendBaseUrl?: string;
|
|
15
|
+
dashboardBaseUrl?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Resolve `backendBaseUrl` + `dashboardBaseUrl` from the running
|
|
18
|
+
* daemon's view (returned in `hello.effectiveConfig`) rather than the
|
|
19
|
+
* CLI's env. Solves the daemon-vs-CLI env-divergence problem when the
|
|
20
|
+
* CLI is launched from a different shell (ssh, IDE-spawned terminal)
|
|
21
|
+
* than the systemd / launchd-launched daemon. Closes §3e⁶
|
|
22
|
+
* F-broker-env-divergence.
|
|
23
|
+
*
|
|
24
|
+
* Mutually exclusive with explicit `--backend-base-url` /
|
|
25
|
+
* `--dashboard-base-url`; the CLI rejects the combination so the
|
|
26
|
+
* operator picks one source of truth.
|
|
27
|
+
*/
|
|
28
|
+
fromDaemon: boolean;
|
|
29
|
+
}
|
|
30
|
+
declare function parseLoginFlags(argv: readonly string[]): LoginFlags;
|
|
11
31
|
declare function runLogin(argv: readonly string[]): Promise<number>;
|
|
12
32
|
declare function runLogout(): Promise<number>;
|
|
13
33
|
declare function runDoctor(): Promise<number>;
|
|
14
34
|
declare function runCli(argv: readonly string[]): Promise<number>;
|
|
15
35
|
|
|
16
|
-
export { runCli, runDoctor, runLogin, runLogout };
|
|
36
|
+
export { parseLoginFlags, runCli, runDoctor, runLogin, runLogout };
|