rework matrixAccounts to attrset keyed by name + matrixPrimaryAccount
This commit is contained in:
parent
dcc059b4b8
commit
63db108516
1 changed files with 90 additions and 31 deletions
|
|
@ -300,20 +300,9 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
options.hyperhive.matrixAccounts = lib.mkOption {
|
options.hyperhive.matrixAccounts = lib.mkOption {
|
||||||
type = lib.types.listOf (
|
type = lib.types.attrsOf (
|
||||||
lib.types.submodule {
|
lib.types.submodule {
|
||||||
options = {
|
options = {
|
||||||
name = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "ccc";
|
|
||||||
description = ''
|
|
||||||
Logical account name the agent addresses this account by
|
|
||||||
(the `account` argument on the matrix MCP tools). Must be
|
|
||||||
unique within the agent. The FIRST account in the list is
|
|
||||||
the agent's primary account --- the one selected when a
|
|
||||||
tool call omits `account`.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
tokenFile = lib.mkOption {
|
tokenFile = lib.mkOption {
|
||||||
type = lib.types.str;
|
type = lib.types.str;
|
||||||
example = "/agents/dmatrix/state/matrix-token-ccc";
|
example = "/agents/dmatrix/state/matrix-token-ccc";
|
||||||
|
|
@ -350,21 +339,50 @@ in
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
default = [ ];
|
default = { };
|
||||||
|
example = lib.literalExpression ''
|
||||||
|
{
|
||||||
|
main = {
|
||||||
|
tokenFile = "/agents/dmatrix/state/matrix-token";
|
||||||
|
sessionDir = "/agents/dmatrix/state/matrix-sdk-state";
|
||||||
|
};
|
||||||
|
ccc = {
|
||||||
|
tokenFile = "/agents/dmatrix/state/matrix-token-ccc";
|
||||||
|
sessionDir = "/agents/dmatrix/state/matrix-sdk-state-ccc";
|
||||||
|
homeserver = "https://matrix.example.org";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
'';
|
||||||
description = ''
|
description = ''
|
||||||
Declare multiple matrix accounts served by a single
|
Declare multiple matrix accounts served by a single
|
||||||
`hive-matrix-daemon` (one matrix-sdk Client + sync loop each),
|
`hive-matrix-daemon` (one matrix-sdk Client + sync loop each),
|
||||||
replacing the wasteful "one MCP server + daemon per account"
|
replacing the wasteful "one MCP server + daemon per account"
|
||||||
pattern. Each entry becomes a routable account the matrix MCP
|
pattern. The attribute name keys each account (unique by
|
||||||
tools can target via their `account` argument; omitting `account`
|
construction) and is the handle the matrix MCP tools target via
|
||||||
on a tool call selects the first (primary) entry.
|
their `account` argument; omitting `account` on a tool call selects
|
||||||
|
`hyperhive.matrixPrimaryAccount`.
|
||||||
|
|
||||||
Leave empty (the default) for the common single-account case: the
|
Leave empty (the default) for the common single-account case: the
|
||||||
daemon then synthesizes one account from `hyperhive.matrix.url` +
|
daemon then synthesizes one account from `hyperhive.matrix.url` +
|
||||||
`<state>/matrix-token` + `<state>/matrix-sdk-state`, so existing
|
`<state>/matrix-token` + `<state>/matrix-sdk-state`, so existing
|
||||||
agents need no change. When non-empty, this list is serialized to
|
agents need no change. When non-empty, the set is serialized to the
|
||||||
the daemon's `HIVE_MATRIX_ACCOUNTS` environment variable and the
|
daemon's `HIVE_MATRIX_ACCOUNTS` environment variable
|
||||||
legacy single-account fallback is bypassed.
|
(primary-account-first) and the legacy single-account fallback is
|
||||||
|
bypassed.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
options.hyperhive.matrixPrimaryAccount = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.str;
|
||||||
|
default = null;
|
||||||
|
example = "main";
|
||||||
|
description = ''
|
||||||
|
Which `hyperhive.matrixAccounts` key is the agent's primary
|
||||||
|
account --- the one the matrix MCP tools act as when a tool call
|
||||||
|
omits its `account` argument. May be left null when exactly one
|
||||||
|
account is declared (that sole account is then primary); it is
|
||||||
|
required (asserted) when more than one account is declared. Ignored
|
||||||
|
when `matrixAccounts` is empty.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -756,6 +774,28 @@ in
|
||||||
assertion = config.hyperhive.icon == null || lib.hasSuffix ".svg" (toString config.hyperhive.icon);
|
assertion = config.hyperhive.icon == null || lib.hasSuffix ".svg" (toString config.hyperhive.icon);
|
||||||
message = "hyperhive.icon must point to an .svg file";
|
message = "hyperhive.icon must point to an .svg file";
|
||||||
}
|
}
|
||||||
|
# With more than one matrix account, the primary must be named
|
||||||
|
# explicitly --- there is no inherent order in the attrset to pick
|
||||||
|
# one from.
|
||||||
|
{
|
||||||
|
assertion =
|
||||||
|
builtins.length (builtins.attrNames config.hyperhive.matrixAccounts) <= 1
|
||||||
|
|| config.hyperhive.matrixPrimaryAccount != null;
|
||||||
|
message =
|
||||||
|
"hyperhive.matrixPrimaryAccount must be set when more than one "
|
||||||
|
+ "hyperhive.matrixAccounts entry is declared (it selects the account "
|
||||||
|
+ "used when a matrix tool call omits `account`).";
|
||||||
|
}
|
||||||
|
# When set, the primary must name an actual declared account.
|
||||||
|
{
|
||||||
|
assertion =
|
||||||
|
config.hyperhive.matrixPrimaryAccount == null
|
||||||
|
|| builtins.hasAttr config.hyperhive.matrixPrimaryAccount config.hyperhive.matrixAccounts;
|
||||||
|
message =
|
||||||
|
"hyperhive.matrixPrimaryAccount (\"${toString config.hyperhive.matrixPrimaryAccount}\") "
|
||||||
|
+ "must be one of the hyperhive.matrixAccounts keys "
|
||||||
|
+ "([ ${lib.concatStringsSep " " (builtins.attrNames config.hyperhive.matrixAccounts)} ]).";
|
||||||
|
}
|
||||||
# hyperhive.frontend.extraFiles[*].target is concatenated into
|
# hyperhive.frontend.extraFiles[*].target is concatenated into
|
||||||
# $out during the mergedDist build. The option's strMatching
|
# $out during the mergedDist build. The option's strMatching
|
||||||
# type already rejects leading `/`, leading `.`, and the
|
# type already rejects leading `/`, leading `.`, and the
|
||||||
|
|
@ -1313,22 +1353,41 @@ in
|
||||||
# daemon parses (`accounts::configured`). Only set when accounts
|
# daemon parses (`accounts::configured`). Only set when accounts
|
||||||
# are declared, so the single-account agents (the common case)
|
# are declared, so the single-account agents (the common case)
|
||||||
# keep hitting the daemon's legacy fallback (which is bypassed the
|
# keep hitting the daemon's legacy fallback (which is bypassed the
|
||||||
# moment HIVE_MATRIX_ACCOUNTS is present). Keys match the daemon's
|
# moment HIVE_MATRIX_ACCOUNTS is present). The `matrixAccounts`
|
||||||
# `AccountCfg` serde shape: name / token_file / state_dir /
|
# attrset is keyed by account name (unique by construction); we
|
||||||
# optional homeserver.
|
# emit a primary-first JSON array (the daemon treats index 0 as the
|
||||||
// lib.optionalAttrs (config.hyperhive.matrixAccounts != [ ]) {
|
# primary) with each entry in the daemon's `AccountCfg` serde shape:
|
||||||
HIVE_MATRIX_ACCOUNTS = builtins.toJSON (
|
# name / token_file / state_dir / optional homeserver.
|
||||||
map (
|
// lib.optionalAttrs (config.hyperhive.matrixAccounts != { }) (
|
||||||
a:
|
let
|
||||||
|
accts = config.hyperhive.matrixAccounts;
|
||||||
|
names = builtins.attrNames accts;
|
||||||
|
# Primary: the explicit option, or the sole account's name when
|
||||||
|
# only one is declared. The assertions below guarantee this
|
||||||
|
# resolves to a real key.
|
||||||
|
primary =
|
||||||
|
if config.hyperhive.matrixPrimaryAccount != null then
|
||||||
|
config.hyperhive.matrixPrimaryAccount
|
||||||
|
else
|
||||||
|
builtins.head names;
|
||||||
|
# Primary first, then the remaining names (attrNames is sorted).
|
||||||
|
ordered = [ primary ] ++ builtins.filter (n: n != primary) names;
|
||||||
|
toEntry =
|
||||||
|
name:
|
||||||
|
let
|
||||||
|
a = accts.${name};
|
||||||
|
in
|
||||||
{
|
{
|
||||||
inherit (a) name;
|
inherit name;
|
||||||
token_file = a.tokenFile;
|
token_file = a.tokenFile;
|
||||||
state_dir = a.sessionDir;
|
state_dir = a.sessionDir;
|
||||||
}
|
}
|
||||||
// lib.optionalAttrs (a.homeserver != null) { inherit (a) homeserver; }
|
// lib.optionalAttrs (a.homeserver != null) { inherit (a) homeserver; };
|
||||||
) config.hyperhive.matrixAccounts
|
in
|
||||||
);
|
{
|
||||||
};
|
HIVE_MATRIX_ACCOUNTS = builtins.toJSON (map toEntry ordered);
|
||||||
|
}
|
||||||
|
);
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ExecStart = "${pkgs.hyperhive}/bin/hive-matrix-daemon";
|
ExecStart = "${pkgs.hyperhive}/bin/hive-matrix-daemon";
|
||||||
Restart = "on-failure";
|
Restart = "on-failure";
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue