swarm containers: run the claude-code the hive pins for agents
This commit is contained in:
parent
1302288474
commit
a169947f15
6 changed files with 65 additions and 2 deletions
|
|
@ -7,6 +7,9 @@ agent's harness installs them from there.
|
||||||
|
|
||||||
Where it runs and how it's enabled: [`services.md`](services.md#claude-login-minter).
|
Where it runs and how it's enabled: [`services.md`](services.md#claude-login-minter).
|
||||||
|
|
||||||
|
The service runs the `claude-code` that `services.hyperhive.c0re.claudeCodePackage`
|
||||||
|
pins for agents, and its container's own nixpkgs' where the hive pins none.
|
||||||
|
|
||||||
## What happens on a login
|
## What happens on a login
|
||||||
|
|
||||||
1. The agent publishes `needs_login` in its turn-state header, and its card
|
1. The agent publishes `needs_login` in its turn-state header, and its card
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,9 @@ config) and an empty directory of its own. The prompt reaches it on stdin as
|
||||||
one user message in `--input-format stream-json`, so that it can carry content
|
one user message in `--input-format stream-json`, so that it can carry content
|
||||||
blocks (see [Shared context](#shared-context)).
|
blocks (see [Shared context](#shared-context)).
|
||||||
|
|
||||||
|
The `claude` is the one `services.hyperhive.c0re.claudeCodePackage` pins for
|
||||||
|
agents, or its container's own nixpkgs' where the hive pins none.
|
||||||
|
|
||||||
On the ACP runtime, each classification is one prompt on a new `opencode acp`
|
On the ACP runtime, each classification is one prompt on a new `opencode acp`
|
||||||
session. Its config and opencode's state live in a directory of its own,
|
session. Its config and opencode's state live in a directory of its own,
|
||||||
removed after it. Its working directory is one empty directory that every
|
removed after it. Its working directory is one empty directory that every
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,12 @@
|
||||||
`nix-collect-garbage` cannot reclaim an old `claude-code` until
|
`nix-collect-garbage` cannot reclaim an old `claude-code` until
|
||||||
every agent has been rebuilt past it and the old generations
|
every agent has been rebuilt past it and the old generations
|
||||||
are gone.
|
are gone.
|
||||||
|
|
||||||
|
The swarm's `swarm-claude-login` and `swarm-review-classifier`
|
||||||
|
containers, where this host runs them, follow the same pin and
|
||||||
|
keep their own nixpkgs' `claude-code` when it is `null`. They are
|
||||||
|
not agents, so pinning an older build for an agent-side
|
||||||
|
regression pins them too.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
dashboardPort = lib.mkOption {
|
dashboardPort = lib.mkOption {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,16 @@
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
hyperhiveCfg = config.services.hyperhive;
|
hyperhiveCfg = config.services.hyperhive;
|
||||||
|
|
||||||
|
# The `claude-code` the agents run, so this container does not trail them on
|
||||||
|
# the CLI; `fallback`, the container's own nixpkgs', only where the hive pins
|
||||||
|
# none.
|
||||||
|
claudeCode =
|
||||||
|
fallback:
|
||||||
|
if hyperhiveCfg.c0re.claudeCodePackage != null then
|
||||||
|
hyperhiveCfg.c0re.claudeCodePackage
|
||||||
|
else
|
||||||
|
fallback;
|
||||||
cfg = hyperhiveCfg.deploy.claude-login;
|
cfg = hyperhiveCfg.deploy.claude-login;
|
||||||
baoCfg = hyperhiveCfg.swarm.bao;
|
baoCfg = hyperhiveCfg.swarm.bao;
|
||||||
baoDeploy = hyperhiveCfg.deploy.bao;
|
baoDeploy = hyperhiveCfg.deploy.bao;
|
||||||
|
|
@ -175,7 +185,7 @@ in
|
||||||
SWARM_CLAUDE_LOGIN_NATS_URL = "tls://${natsCfg.domain}:${toString natsCfg.port}";
|
SWARM_CLAUDE_LOGIN_NATS_URL = "tls://${natsCfg.domain}:${toString natsCfg.port}";
|
||||||
SWARM_CLAUDE_LOGIN_OIDC_TOKEN_ENDPOINT = "${autheliaCfg.url}/api/oidc/token";
|
SWARM_CLAUDE_LOGIN_OIDC_TOKEN_ENDPOINT = "${autheliaCfg.url}/api/oidc/token";
|
||||||
SWARM_CLAUDE_LOGIN_OIDC_CLIENT_ID = clientId;
|
SWARM_CLAUDE_LOGIN_OIDC_CLIENT_ID = clientId;
|
||||||
SWARM_CLAUDE_LOGIN_CLAUDE = "${pkgs.claude-code}/bin/claude";
|
SWARM_CLAUDE_LOGIN_CLAUDE = "${claudeCode pkgs.claude-code}/bin/claude";
|
||||||
}
|
}
|
||||||
// lib.optionalAttrs (storeFiles ? claude-login-bao-server-ca) {
|
// lib.optionalAttrs (storeFiles ? claude-login-bao-server-ca) {
|
||||||
BAO_CACERT = credential "claude-login-bao-server-ca";
|
BAO_CACERT = credential "claude-login-bao-server-ca";
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,16 @@
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
hyperhiveCfg = config.services.hyperhive;
|
hyperhiveCfg = config.services.hyperhive;
|
||||||
|
|
||||||
|
# The `claude-code` the agents run, so this container does not trail them on
|
||||||
|
# the CLI; `fallback`, the container's own nixpkgs', only where the hive pins
|
||||||
|
# none.
|
||||||
|
claudeCode =
|
||||||
|
fallback:
|
||||||
|
if hyperhiveCfg.c0re.claudeCodePackage != null then
|
||||||
|
hyperhiveCfg.c0re.claudeCodePackage
|
||||||
|
else
|
||||||
|
fallback;
|
||||||
cfg = hyperhiveCfg.deploy.review-classifier;
|
cfg = hyperhiveCfg.deploy.review-classifier;
|
||||||
baoCfg = hyperhiveCfg.swarm.bao;
|
baoCfg = hyperhiveCfg.swarm.bao;
|
||||||
baoDeploy = hyperhiveCfg.deploy.bao;
|
baoDeploy = hyperhiveCfg.deploy.bao;
|
||||||
|
|
@ -195,7 +205,7 @@ in
|
||||||
SWARM_REVIEW_CLASSIFIER_NATS_URL = "tls://${natsCfg.domain}:${toString natsCfg.port}";
|
SWARM_REVIEW_CLASSIFIER_NATS_URL = "tls://${natsCfg.domain}:${toString natsCfg.port}";
|
||||||
SWARM_REVIEW_CLASSIFIER_OIDC_TOKEN_ENDPOINT = "${autheliaCfg.url}/api/oidc/token";
|
SWARM_REVIEW_CLASSIFIER_OIDC_TOKEN_ENDPOINT = "${autheliaCfg.url}/api/oidc/token";
|
||||||
SWARM_REVIEW_CLASSIFIER_OIDC_CLIENT_ID = clientId;
|
SWARM_REVIEW_CLASSIFIER_OIDC_CLIENT_ID = clientId;
|
||||||
SWARM_REVIEW_CLASSIFIER_CLAUDE = "${pkgs.claude-code}/bin/claude";
|
SWARM_REVIEW_CLASSIFIER_CLAUDE = "${claudeCode pkgs.claude-code}/bin/claude";
|
||||||
SWARM_REVIEW_CLASSIFIER_OPENCODE = "${pkgs.opencode}/bin/opencode";
|
SWARM_REVIEW_CLASSIFIER_OPENCODE = "${pkgs.opencode}/bin/opencode";
|
||||||
}
|
}
|
||||||
// lib.optionalAttrs (storeFiles ? review-classifier-bao-server-ca) {
|
// lib.optionalAttrs (storeFiles ? review-classifier-bao-server-ca) {
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,13 @@ let
|
||||||
deploy.builder.enable = false;
|
deploy.builder.enable = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Same, with the claude-code build the agents run pinned on the hive. Any
|
||||||
|
# package serves: only the path the containers are pointed at is read.
|
||||||
|
swarmServicesPinnedClaude = hiveWithController {
|
||||||
|
deploy.allSwarmServices = true;
|
||||||
|
c0re.claudeCodePackage = pkgs.hello;
|
||||||
|
};
|
||||||
|
|
||||||
# Same, with one of those services placed on another host. Every derivation
|
# Same, with one of those services placed on another host. Every derivation
|
||||||
# in that module is `mkDefault` so this stays expressible.
|
# in that module is `mkDefault` so this stays expressible.
|
||||||
swarmServicesBaoElsewhere = hive {
|
swarmServicesBaoElsewhere = hive {
|
||||||
|
|
@ -273,6 +280,30 @@ let
|
||||||
&& !(swarmServicesBaoElsewhere.containers ? swarm-claude-login)
|
&& !(swarmServicesBaoElsewhere.containers ? swarm-claude-login)
|
||||||
&& swarmServicesBaoElsewhere.services.hyperhive.deploy.nats.enable;
|
&& swarmServicesBaoElsewhere.services.hyperhive.deploy.nats.enable;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
# The CLI is the one the agents run, not the container's own nixpkgs'.
|
||||||
|
# Unpinned is the control: it falls back to the container's own `claude-code`.
|
||||||
|
name = "the claude login minter and the review classifier run the claude-code the hive pins";
|
||||||
|
ok =
|
||||||
|
let
|
||||||
|
claudeOf =
|
||||||
|
host: container: unit: var:
|
||||||
|
host.containers.${container}.config.systemd.services.${unit}.environment.${var};
|
||||||
|
pinned = "${pkgs.hello}/bin/claude";
|
||||||
|
in
|
||||||
|
claudeOf swarmServicesPinnedClaude "swarm-claude-login" "swarm-claude-login"
|
||||||
|
"SWARM_CLAUDE_LOGIN_CLAUDE" == pinned
|
||||||
|
&&
|
||||||
|
claudeOf swarmServicesPinnedClaude "swarm-review-classifier" "swarm-review-classifier"
|
||||||
|
"SWARM_REVIEW_CLASSIFIER_CLAUDE" == pinned
|
||||||
|
&& lib.hasInfix "claude-code" (
|
||||||
|
claudeOf swarmServicesHere "swarm-claude-login" "swarm-claude-login" "SWARM_CLAUDE_LOGIN_CLAUDE"
|
||||||
|
)
|
||||||
|
&& lib.hasInfix "claude-code" (
|
||||||
|
claudeOf swarmServicesHere "swarm-review-classifier" "swarm-review-classifier"
|
||||||
|
"SWARM_REVIEW_CLASSIFIER_CLAUDE"
|
||||||
|
);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
# The switch fills in for an operator who has not spoken and yields to
|
# The switch fills in for an operator who has not spoken and yields to
|
||||||
# one who has — that is what keeps a shared service placeable on a host
|
# one who has — that is what keeps a shared service placeable on a host
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue