@themoltnet/agent-daemon 0.59.0 → 0.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -11
- package/dist/cli.js +115 -81
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -114,7 +114,7 @@ All config flows from environment variables. The daemon reads them in
|
|
|
114
114
|
| Var | Required | Purpose |
|
|
115
115
|
| --------------------- | ---------------------------------- | ------------------------------------------------------------------------- |
|
|
116
116
|
| `GIT_CONFIG_GLOBAL` | config-based | Optional git identity path; not needed for configless startup. |
|
|
117
|
-
| `MOLTNET_AGENT_NAME` | yes |
|
|
117
|
+
| `MOLTNET_AGENT_NAME` | yes | Central identity alias (`identities/<name>/`). |
|
|
118
118
|
| `MOLTNET_API_URL` | configless only | Explicit API endpoint; configless runs never read it from `moltnet.json`. |
|
|
119
119
|
| `MOLTNET_AGENT_KEY` | no | Team- or identity-scoped agent key. Overrides `moltnet.json`. |
|
|
120
120
|
| `MOLTNET_PRIVATE_KEY` | configless `once`, `poll`, `drain` | Base64 Ed25519 seed used by daemon-owned executor attestation. |
|
|
@@ -133,18 +133,19 @@ its gitconfig in `.moltnet/<agent>/`. Three paths create it:
|
|
|
133
133
|
does the same for coding agents that need git and GitHub.
|
|
134
134
|
|
|
135
135
|
**The daemon runs on an agent key only.** OAuth2 client_credentials is not
|
|
136
|
-
accepted: it hands the daemon the full
|
|
136
|
+
accepted: it hands the daemon the full agent OAuth2 grant against a least-privilege
|
|
137
137
|
need, and a Hydra token cannot be a Talos derivation parent. A `moltnet.json`
|
|
138
|
-
without `agent_key_ref` is refused at startup with the command that fixes it.
|
|
138
|
+
without `agent_key_ref` or `agent_key_refs` is refused at startup with the command that fixes it.
|
|
139
139
|
|
|
140
140
|
The key reaches the daemon two ways, and `MOLTNET_AGENT_KEY` wins when both are
|
|
141
141
|
present:
|
|
142
142
|
|
|
143
143
|
- **Configless** — `MOLTNET_AGENT_KEY` (or `MOLTNET_AGENT_KEY_REF`) in the
|
|
144
144
|
environment. No agent files are read at all.
|
|
145
|
-
- **From `moltnet.json`** —
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
- **From `moltnet.json`** — `agent_key_refs[teamId]` selects the run team's
|
|
146
|
+
provider reference. `agent_key_ref` is used only if that team has no entry.
|
|
147
|
+
A configured entry that fails never falls back. The plaintext secret never
|
|
148
|
+
lands in the file.
|
|
148
149
|
|
|
149
150
|
Mint or rotate the key with the CLI, which is separate operator tooling and
|
|
150
151
|
keeps using OAuth2 for its own authentication:
|
|
@@ -155,14 +156,21 @@ moltnet agents keys create --agent-id <uuid> --team-id <uuid> \
|
|
|
155
156
|
moltnet agents keys rotate <key-id> --team-id <uuid> --store
|
|
156
157
|
```
|
|
157
158
|
|
|
159
|
+
To enroll the same identity into another team, use
|
|
160
|
+
`moltnet teams join --code <code> --issue-agent-key --store --idempotency-key <uuid>`.
|
|
161
|
+
Each Agent Server run selects its `teamId` before activation and profile lookup;
|
|
162
|
+
concurrent runs do not change a shared team selector. New runs reload credentials,
|
|
163
|
+
so restart active runs after rotating a key. See the
|
|
164
|
+
[enrollment and recovery guide](../../docs/operate/agent-keys.md#enroll-an-existing-agent-into-another-team).
|
|
165
|
+
|
|
158
166
|
The daemon reconciles a team-bound key against `--team` at startup; an
|
|
159
167
|
identity-scoped key may select any team where the agent is authorized. It fails
|
|
160
168
|
fast if the key is rejected, is not an agent, or a team binding mismatches. See
|
|
161
169
|
[Run the daemon with an agent key](../../docs/operate/agent-keys.md#run-the-daemon-with-an-agent-key).
|
|
162
170
|
|
|
163
171
|
Daemon authentication and the guest boundary are two separate concerns. Where
|
|
164
|
-
the agent key comes from (the environment, or
|
|
165
|
-
|
|
172
|
+
the agent key comes from (the environment, or a selected key reference in the
|
|
173
|
+
central identity's `moltnet.json` resolved through the host secret provider)
|
|
166
174
|
decides how the host-side SDK `Agent` is built. The guest boundary is fixed: **the guest never
|
|
167
175
|
receives MoltNet credential material.** No `.moltnet` file, gitconfig, SSH
|
|
168
176
|
signing key, GitHub App PEM, or MoltNet environment credential is injected into
|
|
@@ -196,9 +204,13 @@ An agent key used by the daemon needs this least-privilege scope set:
|
|
|
196
204
|
agent:profile crypto:sign runtime:read task:read task:claim task:execute
|
|
197
205
|
```
|
|
198
206
|
|
|
199
|
-
The
|
|
200
|
-
|
|
201
|
-
|
|
207
|
+
The daemon also uses `diary:read`, `team:read` and `team:join` when the key
|
|
208
|
+
carries them -- read access to the agent's teams and their diaries, and
|
|
209
|
+
enrollment into a team. Startup does not check for them: a key without them
|
|
210
|
+
claims and runs work, and the daemon reports which are absent.
|
|
211
|
+
|
|
212
|
+
The Console selects the full set by default. Knowledge-enabled workers must add
|
|
213
|
+
`diary:write`, `pack:read`, and `pack:write` when the key is issued.
|
|
202
214
|
|
|
203
215
|
`crypto:sign` is in the minimum because host-capability signing runs on the
|
|
204
216
|
daemon's own credential: the local seed signer calls the signing-request
|
package/dist/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ import { AgentRuntime, ApiTaskReporter, ApiTaskSource, PollingApiTaskSource, cre
|
|
|
14
14
|
import { GuestEnvironmentBoundaryError, assertGuestEnvironmentBoundary, createPiRetryTriage, findMainWorktree, isResolvedPathInsideRoot, normalizeRetryTriageResult, redactRetryTriageSecrets, resolveRuntimeProfileModel } from "@themoltnet/pi-runtime";
|
|
15
15
|
import { FILE_SECRET_PROVIDER, FileSecretProvider, RegisterIdentityError, boundedIdentitySignal, connect, createNodeSecretProviderRegistry, register } from "@themoltnet/sdk/node";
|
|
16
16
|
import { constants, copyFileSync, createReadStream, createWriteStream, existsSync, lstatSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
17
|
-
import { AuthenticationError, IDENTITY_ALIAS_PATTERN, MoltNetError, assertIdentityAlias, assertTrustedConfigApiUrl, createExecutorAttestor, formatSecretReferenceString, getConfigDir, getIdentityDir, isCanonicalConfig, parseSecretReferenceString, readConfig, requireSecureCredentialApiUrl, resolveAgentKey, resolveEnvSecretReference, resolveIdentitySeed } from "@themoltnet/sdk";
|
|
17
|
+
import { AuthenticationError, IDENTITY_ALIAS_PATTERN, MoltNetError, assertIdentityAlias, assertTrustedConfigApiUrl, createExecutorAttestor, formatSecretReferenceString, getConfigDir, getIdentityDir, hasAgentKeyConfiguration, isCanonicalConfig, parseSecretReferenceString, readConfig, requireSecureCredentialApiUrl, resolveAgentKey, resolveEnvSecretReference, resolveIdentitySeed, selectAgentKeyReference } from "@themoltnet/sdk";
|
|
18
18
|
import { execFile, spawn } from "node:child_process";
|
|
19
19
|
import { X509Certificate, createHash, createPrivateKey, createPublicKey, randomBytes, randomUUID, timingSafeEqual, webcrypto } from "node:crypto";
|
|
20
20
|
import { once } from "node:events";
|
|
@@ -774,13 +774,24 @@ var CREDENTIAL_SCOPES = {
|
|
|
774
774
|
TaskManage: "task:manage",
|
|
775
775
|
TaskRead: "task:read",
|
|
776
776
|
TaskWrite: "task:write",
|
|
777
|
+
TeamJoin: "team:join",
|
|
777
778
|
TeamManage: "team:manage",
|
|
778
779
|
TeamRead: "team:read"
|
|
779
780
|
};
|
|
780
781
|
var ALL_CREDENTIAL_SCOPES = Object.freeze(Object.values(CREDENTIAL_SCOPES));
|
|
781
782
|
/**
|
|
782
|
-
*
|
|
783
|
-
*
|
|
783
|
+
* What the agent daemon cannot run without, checked against
|
|
784
|
+
* `GET /agents/whoami` at startup. Task credentials attenuate it further to
|
|
785
|
+
* `task:execute` alone.
|
|
786
|
+
*
|
|
787
|
+
* This is the **boot floor**, and deliberately not the same list as
|
|
788
|
+
* `AGENT_CREDENTIAL_SCOPES`. A credential's scopes are fixed when it is minted
|
|
789
|
+
* and `POST /agent-keys` caps a new key at the scopes of the credential
|
|
790
|
+
* requesting it, so no key can ever widen itself. A scope added here therefore
|
|
791
|
+
* stops every daemon already in the field, and only a human with a Console
|
|
792
|
+
* session can mint the replacement. Add one only when the daemon genuinely
|
|
793
|
+
* cannot work without it; anything a caller merely benefits from belongs in
|
|
794
|
+
* `DAEMON_OPTIONAL_SCOPES`, where absence costs a capability instead.
|
|
784
795
|
*
|
|
785
796
|
* `crypto:sign` is part of the minimum because host-capability signing runs on
|
|
786
797
|
* the daemon's own credential: the local seed signer calls the signing-request
|
|
@@ -788,7 +799,7 @@ var ALL_CREDENTIAL_SCOPES = Object.freeze(Object.values(CREDENTIAL_SCOPES));
|
|
|
788
799
|
* cleanly and then fails the first time guest code signs a diary entry or a
|
|
789
800
|
* commit.
|
|
790
801
|
*/
|
|
791
|
-
var
|
|
802
|
+
var DAEMON_MINIMUM_SCOPES = [
|
|
792
803
|
CREDENTIAL_SCOPES.AgentProfile,
|
|
793
804
|
CREDENTIAL_SCOPES.CryptoSign,
|
|
794
805
|
CREDENTIAL_SCOPES.RuntimeRead,
|
|
@@ -796,6 +807,22 @@ var AGENT_CREDENTIAL_SCOPES = [
|
|
|
796
807
|
CREDENTIAL_SCOPES.TaskClaim,
|
|
797
808
|
CREDENTIAL_SCOPES.TaskExecute
|
|
798
809
|
];
|
|
810
|
+
/**
|
|
811
|
+
* Read and enrollment authority a daemon uses when it has it, and runs without
|
|
812
|
+
* when it does not: reading the teams it belongs to and their diaries, and
|
|
813
|
+
* joining a team it is not yet a member of.
|
|
814
|
+
*
|
|
815
|
+
* Which product surface each one enables is deliberately not recorded here.
|
|
816
|
+
* That mapping belongs to whatever consumes the scope and changes with it,
|
|
817
|
+
* while the scope names are the contract and do not.
|
|
818
|
+
*/
|
|
819
|
+
var DAEMON_OPTIONAL_SCOPES = [
|
|
820
|
+
CREDENTIAL_SCOPES.DiaryRead,
|
|
821
|
+
CREDENTIAL_SCOPES.TeamRead,
|
|
822
|
+
CREDENTIAL_SCOPES.TeamJoin
|
|
823
|
+
];
|
|
824
|
+
/** What a newly issued agent key should carry: the floor plus the rest. */
|
|
825
|
+
var AGENT_CREDENTIAL_SCOPES = [...DAEMON_MINIMUM_SCOPES, ...DAEMON_OPTIONAL_SCOPES];
|
|
799
826
|
CREDENTIAL_SCOPES.AgentProfile, CREDENTIAL_SCOPES.TaskRead, CREDENTIAL_SCOPES.TaskWrite;
|
|
800
827
|
CREDENTIAL_SCOPES.AgentProfile, CREDENTIAL_SCOPES.DiaryRead, CREDENTIAL_SCOPES.PackRead, CREDENTIAL_SCOPES.RuntimeRead, CREDENTIAL_SCOPES.TaskRead, CREDENTIAL_SCOPES.TeamRead;
|
|
801
828
|
Object.freeze(ALL_CREDENTIAL_SCOPES.filter((scope) => scope !== CREDENTIAL_SCOPES.HumanProfile));
|
|
@@ -819,6 +846,7 @@ var MCP_CLIENT_SCOPES = [
|
|
|
819
846
|
CREDENTIAL_SCOPES.TaskManage,
|
|
820
847
|
CREDENTIAL_SCOPES.TaskRead,
|
|
821
848
|
CREDENTIAL_SCOPES.TaskWrite,
|
|
849
|
+
CREDENTIAL_SCOPES.TeamJoin,
|
|
822
850
|
CREDENTIAL_SCOPES.TeamManage,
|
|
823
851
|
CREDENTIAL_SCOPES.TeamRead
|
|
824
852
|
];
|
|
@@ -1172,17 +1200,16 @@ Type.Object({
|
|
|
1172
1200
|
Type.Literal("executor"),
|
|
1173
1201
|
Type.Literal("member")
|
|
1174
1202
|
])),
|
|
1175
|
-
maxUses: Type.Optional(Type.Integer({
|
|
1176
|
-
minimum: 1,
|
|
1177
|
-
default: 1
|
|
1178
|
-
})),
|
|
1179
1203
|
expiresInHours: Type.Optional(Type.Integer({
|
|
1180
1204
|
minimum: 1,
|
|
1181
1205
|
maximum: 720,
|
|
1182
1206
|
default: 168
|
|
1183
1207
|
}))
|
|
1184
1208
|
});
|
|
1185
|
-
Type.Object({
|
|
1209
|
+
Type.Object({
|
|
1210
|
+
code: Type.String({ minLength: 1 }),
|
|
1211
|
+
issueAgentKey: Type.Optional(Type.Literal(true))
|
|
1212
|
+
});
|
|
1186
1213
|
Type.Object({ role: Type.Union([
|
|
1187
1214
|
Type.Literal("manager"),
|
|
1188
1215
|
Type.Literal("executor"),
|
|
@@ -1207,8 +1234,7 @@ Type.Object({
|
|
|
1207
1234
|
Type.Literal("executor"),
|
|
1208
1235
|
Type.Literal("member")
|
|
1209
1236
|
]),
|
|
1210
|
-
|
|
1211
|
-
useCount: Type.Integer(),
|
|
1237
|
+
usedAt: Type.Union([Type.String({ format: "date-time" }), Type.Null()]),
|
|
1212
1238
|
expiresAt: DateTimeUnsafe,
|
|
1213
1239
|
createdAt: DateTimeUnsafe
|
|
1214
1240
|
});
|
|
@@ -1239,11 +1265,7 @@ Type.Object({
|
|
|
1239
1265
|
});
|
|
1240
1266
|
Type.Object({
|
|
1241
1267
|
teamId: UuidSchema,
|
|
1242
|
-
role:
|
|
1243
|
-
Type.Literal("manager"),
|
|
1244
|
-
Type.Literal("executor"),
|
|
1245
|
-
Type.Literal("member")
|
|
1246
|
-
])
|
|
1268
|
+
role: TeamRoleSchema
|
|
1247
1269
|
});
|
|
1248
1270
|
Type.Object({
|
|
1249
1271
|
updated: Type.Boolean(),
|
|
@@ -3501,10 +3523,11 @@ Run \`agent-daemon <command> --help\` for command-specific flags.
|
|
|
3501
3523
|
Prerequisites:
|
|
3502
3524
|
- configless: MOLTNET_AGENT_KEY (or MOLTNET_AGENT_KEY_REF) and
|
|
3503
3525
|
MOLTNET_PRIVATE_KEY (or MOLTNET_PRIVATE_KEY_REF); no agent files
|
|
3504
|
-
- config-based
|
|
3505
|
-
carrying agent_key_ref (OAuth2
|
|
3526
|
+
- config-based: ~/.config/moltnet/identities/<agent>/moltnet.json
|
|
3527
|
+
carrying agent_key_refs or agent_key_ref (OAuth2 is not accepted)
|
|
3528
|
+
--agent-root explicitly selects a legacy .moltnet/<agent> bundle
|
|
3506
3529
|
|
|
3507
|
-
No key yet? Mint one with the CLI (--store writes
|
|
3530
|
+
No key yet? Mint one with the CLI (--store writes the team slot into
|
|
3508
3531
|
moltnet.json and keeps the secret in a provider):
|
|
3509
3532
|
|
|
3510
3533
|
moltnet teams list # find the team id
|
|
@@ -3712,6 +3735,10 @@ function assessAgentStartupPin(current, expected) {
|
|
|
3712
3735
|
};
|
|
3713
3736
|
return { ok: true };
|
|
3714
3737
|
}
|
|
3738
|
+
/** A configured team slot must authenticate with that exact team ceiling. */
|
|
3739
|
+
function matchesCredentialTeam(current, credentialTeamId) {
|
|
3740
|
+
return !credentialTeamId || current.credentialBinding?.bindingScope === "team" && current.credentialBinding.boundTeamId === credentialTeamId;
|
|
3741
|
+
}
|
|
3715
3742
|
//#endregion
|
|
3716
3743
|
//#region src/lib/agent-context.ts
|
|
3717
3744
|
/**
|
|
@@ -3755,7 +3782,7 @@ function detectCredentialSource(env) {
|
|
|
3755
3782
|
function assessStartupBinding(whoami, teamId) {
|
|
3756
3783
|
if (whoami.subjectType !== "agent") return {
|
|
3757
3784
|
ok: false,
|
|
3758
|
-
reason: `the daemon must authenticate as an agent, but whoami reported subjectType "${whoami.subjectType}". Provide agent credentials (an agent key
|
|
3785
|
+
reason: `the daemon must authenticate as an agent, but whoami reported subjectType "${whoami.subjectType}". Provide agent credentials (an agent key).`
|
|
3759
3786
|
};
|
|
3760
3787
|
const boundTeamId = whoami.credentialBinding?.bindingScope === "team" ? whoami.credentialBinding.boundTeamId : void 0;
|
|
3761
3788
|
if (teamId && boundTeamId && boundTeamId !== teamId) return {
|
|
@@ -3768,8 +3795,7 @@ function assessStartupBinding(whoami, teamId) {
|
|
|
3768
3795
|
* Validate at startup — after `connect()`, before polling — that the connected
|
|
3769
3796
|
* credential can operate as `teamId`, failing fast with an actionable message
|
|
3770
3797
|
* instead of letting an obscure 401/403 surface mid-poll. Runs in both auth
|
|
3771
|
-
*
|
|
3772
|
-
* subject-type check. Returns the `whoami` so the caller can log the resolved
|
|
3798
|
+
* credential sources and also checks API reachability and subject type. Returns the `whoami` so the caller can log the resolved
|
|
3773
3799
|
* identity (never the secret).
|
|
3774
3800
|
*/
|
|
3775
3801
|
async function validateStartupBinding(options) {
|
|
@@ -3785,6 +3811,7 @@ async function validateStartupBinding(options) {
|
|
|
3785
3811
|
setTimeout(resolve, 100 * attempt);
|
|
3786
3812
|
});
|
|
3787
3813
|
}
|
|
3814
|
+
if (!matchesCredentialTeam(whoami, options.credentialTeamId)) throw new Error("Daemon startup validation failed: selected team credential has a different binding.");
|
|
3788
3815
|
const assessment = assessStartupBinding(whoami, options.teamId);
|
|
3789
3816
|
if (!assessment.ok) throw new Error(`Daemon startup validation failed: ${assessment.reason}`);
|
|
3790
3817
|
const expected = options.expectedAgent;
|
|
@@ -3807,9 +3834,9 @@ async function resolveAgentContext(agentName, options = {}) {
|
|
|
3807
3834
|
credentialSource: "environment"
|
|
3808
3835
|
};
|
|
3809
3836
|
const config = await readConfig(agentDir);
|
|
3810
|
-
if (!config
|
|
3837
|
+
if (!config || !hasAgentKeyConfiguration(config)) throw new Error(agentKeyRequiredMessage(agentDir, agentName));
|
|
3811
3838
|
const secretProviders = createNodeSecretProviderRegistry();
|
|
3812
|
-
const agentKey = await resolveAgentKey(config, secretProviders);
|
|
3839
|
+
const agentKey = await resolveAgentKey(config, secretProviders, options.teamId);
|
|
3813
3840
|
if (!agentKey) throw new Error(agentKeyRequiredMessage(agentDir, agentName));
|
|
3814
3841
|
return {
|
|
3815
3842
|
agentDir,
|
|
@@ -3820,7 +3847,8 @@ async function resolveAgentContext(agentName, options = {}) {
|
|
|
3820
3847
|
agentKey,
|
|
3821
3848
|
apiUrl: resolveConfigApiUrl(config, options.envApiUrl)
|
|
3822
3849
|
}),
|
|
3823
|
-
credentialSource: "config"
|
|
3850
|
+
credentialSource: "config",
|
|
3851
|
+
credentialTeamId: selectAgentKeyReference(config, options.teamId)?.teamId
|
|
3824
3852
|
};
|
|
3825
3853
|
}
|
|
3826
3854
|
/**
|
|
@@ -3831,7 +3859,7 @@ async function resolveAgentContext(agentName, options = {}) {
|
|
|
3831
3859
|
*/
|
|
3832
3860
|
function agentKeyRequiredMessage(agentDir, agentName) {
|
|
3833
3861
|
return [
|
|
3834
|
-
`${join(agentDir, "moltnet.json")} has no "agent_key_ref". The daemon`,
|
|
3862
|
+
`${join(agentDir, "moltnet.json")} has no "agent_key_ref" or "agent_key_refs". The daemon`,
|
|
3835
3863
|
`requires a team-bound agent key; OAuth2 client_credentials is no longer`,
|
|
3836
3864
|
`accepted.`,
|
|
3837
3865
|
``,
|
|
@@ -3844,7 +3872,7 @@ function agentKeyRequiredMessage(agentDir, agentName) {
|
|
|
3844
3872
|
` --scopes ${DAEMON_KEY_SCOPES.join(",")} \\`,
|
|
3845
3873
|
` --store`,
|
|
3846
3874
|
``,
|
|
3847
|
-
`--store writes "
|
|
3875
|
+
`--store writes the team slot in "agent_key_refs" and keeps the secret in`,
|
|
3848
3876
|
`a provider, so the key itself never lands in the file. Omit --scopes to`,
|
|
3849
3877
|
`get the same daemon minimum by default.`,
|
|
3850
3878
|
``,
|
|
@@ -4558,7 +4586,14 @@ function recoverScratchWorkspacePath(producer, stateDirs) {
|
|
|
4558
4586
|
}
|
|
4559
4587
|
//#endregion
|
|
4560
4588
|
//#region src/lib/executor-attestation.ts
|
|
4561
|
-
|
|
4589
|
+
/**
|
|
4590
|
+
* The startup gate, which is the boot floor rather than the issuance default.
|
|
4591
|
+
* Gating on the wider `AGENT_CREDENTIAL_SCOPES` would refuse every key minted
|
|
4592
|
+
* before a scope was added to it, and a key cannot widen itself.
|
|
4593
|
+
*/
|
|
4594
|
+
var DAEMON_REQUIRED_SCOPES = DAEMON_MINIMUM_SCOPES;
|
|
4595
|
+
/** What `moltnet agents keys create` should mint for a new daemon. */
|
|
4596
|
+
var DAEMON_RECOMMENDED_SCOPES = AGENT_CREDENTIAL_SCOPES;
|
|
4562
4597
|
async function resolveExecutorSigningPrivateKey(input) {
|
|
4563
4598
|
if (input.credentialSource === "environment") {
|
|
4564
4599
|
const privateKey = input.configuredPrivateKey.trim();
|
|
@@ -4585,7 +4620,7 @@ async function resolveExecutorSigningPrivateKey(input) {
|
|
|
4585
4620
|
function validateDaemonScopes(whoami) {
|
|
4586
4621
|
const available = new Set(whoami.scopes ?? []);
|
|
4587
4622
|
const missing = DAEMON_REQUIRED_SCOPES.filter((scope) => !available.has(scope));
|
|
4588
|
-
if (missing.length > 0) throw new Error(`Daemon startup credential is missing required scopes: ${missing.join(" ")}. Issue a replacement credential with ${
|
|
4623
|
+
if (missing.length > 0) throw new Error(`Daemon startup credential is missing required scopes: ${missing.join(" ")}. Issue a replacement credential with ${DAEMON_RECOMMENDED_SCOPES.join(" ")}.`);
|
|
4589
4624
|
}
|
|
4590
4625
|
async function validateExecutorSigningIdentity(input) {
|
|
4591
4626
|
let publicKey;
|
|
@@ -6632,11 +6667,13 @@ async function runPolling(opts) {
|
|
|
6632
6667
|
const resolvedContext = await resolveAgentContext(identity.agent, {
|
|
6633
6668
|
agentRootDir: explicitAgentRootDir,
|
|
6634
6669
|
credentialSource: cfg.credentialSource,
|
|
6635
|
-
envApiUrl: cfg.apiUrl
|
|
6670
|
+
envApiUrl: cfg.apiUrl,
|
|
6671
|
+
teamId
|
|
6636
6672
|
});
|
|
6637
6673
|
gate = "authenticate_and_bind";
|
|
6638
6674
|
const whoami = await validateStartupBinding({
|
|
6639
6675
|
agent: resolvedContext.agent,
|
|
6676
|
+
credentialTeamId: resolvedContext.credentialTeamId,
|
|
6640
6677
|
teamId,
|
|
6641
6678
|
expectedAgent: cfg.expectedAgent
|
|
6642
6679
|
});
|
|
@@ -7239,11 +7276,13 @@ async function runOnce(argv, runtimeAdapter = defaultPiDaemonAdapter) {
|
|
|
7239
7276
|
const resolvedContext = await resolveAgentContext(identity.agent, {
|
|
7240
7277
|
agentRootDir: explicitAgentRootDir,
|
|
7241
7278
|
credentialSource: cfg.credentialSource,
|
|
7242
|
-
envApiUrl: cfg.apiUrl
|
|
7279
|
+
envApiUrl: cfg.apiUrl,
|
|
7280
|
+
teamId: values.team
|
|
7243
7281
|
});
|
|
7244
7282
|
gate = "authenticate_and_bind";
|
|
7245
7283
|
const whoami = await validateStartupBinding({
|
|
7246
7284
|
agent: resolvedContext.agent,
|
|
7285
|
+
credentialTeamId: resolvedContext.credentialTeamId,
|
|
7247
7286
|
teamId: values.team,
|
|
7248
7287
|
expectedAgent: cfg.expectedAgent
|
|
7249
7288
|
});
|
|
@@ -9540,9 +9579,10 @@ async function createManagedAgent(store, secrets, input, connectAgent = connect)
|
|
|
9540
9579
|
*/
|
|
9541
9580
|
function incompleteRegistrationMessage(alias, known) {
|
|
9542
9581
|
const endpoint = `POST /v1/agents/${alias}/reconcile`;
|
|
9582
|
+
const recovery = known.recoveryPath ? ` Inspect the protected recovery file at ${known.recoveryPath}; it may be incomplete.` : "";
|
|
9543
9583
|
const reconcile = known.configPath ? `Finish it with ${endpoint} and {"action":"resume"}, or discard the local record with {"action":"abandon"}.` : `No local config was written, so it cannot be resumed; discard the local record with ${endpoint} and {"action":"abandon"}.`;
|
|
9544
|
-
if (known.subjectId) return `the remote agent ${known.subjectId} was registered but local activation is incomplete. ${reconcile}`;
|
|
9545
|
-
return `registration for "${alias}" may have completed on the server${known.fingerprint ? ` (fingerprint ${known.fingerprint})` : ""}; look the agent up first. ${reconcile}`;
|
|
9584
|
+
if (known.subjectId) return `the remote agent ${known.subjectId} was registered but local activation is incomplete. ${reconcile}${recovery}`;
|
|
9585
|
+
return `registration for "${alias}" may have completed on the server${known.fingerprint ? ` (fingerprint ${known.fingerprint})` : ""}; look the agent up first. ${reconcile}${recovery}`;
|
|
9546
9586
|
}
|
|
9547
9587
|
/** Resume a fully persisted registration or explicitly abandon local recovery. */
|
|
9548
9588
|
async function reconcileManagedRegistration(store, secrets, aliasInput, action, connectAgent = connect, signal) {
|
|
@@ -9563,8 +9603,9 @@ async function reconcileManagedRegistration(store, secrets, aliasInput, action,
|
|
|
9563
9603
|
store.clearPendingRegistration(alias);
|
|
9564
9604
|
return null;
|
|
9565
9605
|
}
|
|
9566
|
-
|
|
9567
|
-
|
|
9606
|
+
const reference = config ? selectAgentKeyReference(config)?.reference : void 0;
|
|
9607
|
+
if (!config || !reference || reference.provider !== FILE_SECRET_PROVIDER || config.keys.private_key_ref?.provider !== FILE_SECRET_PROVIDER) throw new AgentServerIdentityError("registration_incomplete", `pending registration for "${alias}" does not have complete managed references`);
|
|
9608
|
+
const [agentKey, privateKeyState] = await Promise.all([secrets.read(reference.key), secrets.probe(config.keys.private_key_ref.key)]);
|
|
9568
9609
|
if (!agentKey || privateKeyState !== "present") throw new AgentServerIdentityError("registration_incomplete", `pending registration for "${alias}" is missing persisted secret material`);
|
|
9569
9610
|
const apiUrl = requireConfigApiUrl(config, store.agentPath(alias));
|
|
9570
9611
|
const whoami = await callWhoami(connectAgent, {
|
|
@@ -9600,9 +9641,9 @@ async function attachExternalAgent(store, secretProviders, input, connectAgent =
|
|
|
9600
9641
|
try {
|
|
9601
9642
|
if (!central) externalAgentLocation(configPath);
|
|
9602
9643
|
const config = await readCurrentConfig(configPath);
|
|
9603
|
-
if (central && !config
|
|
9644
|
+
if (central && !hasAgentKeyConfiguration(config)) throw new AgentServerIdentityError("unsupported_credential", `central identity "${alias}" needs a stored agent key before the Agent Server can run it`);
|
|
9604
9645
|
const configApiUrl = requireTrustedConfigApiUrl(config, configPath);
|
|
9605
|
-
const whoami = await authenticateConfig(config, configPath, requireTrustedApiOverride(input.apiUrl, configApiUrl, configPath), secretProviders, connectAgent, input.signal);
|
|
9646
|
+
const whoami = await authenticateConfig(config, configPath, requireTrustedApiOverride(input.apiUrl, configApiUrl, configPath), secretProviders, connectAgent, input.signal, input.teamId);
|
|
9606
9647
|
const identity = identityFromConfig(config);
|
|
9607
9648
|
assertIdentityMatches(identity, whoami, `external config ${configPath}`, "authenticated whoami");
|
|
9608
9649
|
assertSubjectMatches(whoami, config, "authenticated whoami", `external config ${configPath}`);
|
|
@@ -9629,15 +9670,16 @@ async function attachExternalAgent(store, secretProviders, input, connectAgent =
|
|
|
9629
9670
|
}
|
|
9630
9671
|
}
|
|
9631
9672
|
/** Load and authenticate the current config, then refresh its derived pin. */
|
|
9632
|
-
async function verifyAgentActivation(store, alias, managedSecretProviders, externalSecretProviders, connectAgent = connect, signal) {
|
|
9673
|
+
async function verifyAgentActivation(store, alias, managedSecretProviders, externalSecretProviders, connectAgent = connect, signal, teamId) {
|
|
9633
9674
|
const activation = requireActivation(store, alias);
|
|
9634
|
-
|
|
9675
|
+
teamId ??= activation.boundTeamId;
|
|
9676
|
+
const verified = activation.source === "managed" ? await verifyManagedActivation(store, activation, managedSecretProviders, connectAgent, signal, teamId) : await verifyExternalActivation(store, activation, externalSecretProviders, connectAgent, signal, teamId);
|
|
9635
9677
|
assertSubjectMatches(verified.whoami, verified.config, "authenticated whoami", `agent "${activation.alias}" config`);
|
|
9636
9678
|
if (verified.whoami.subjectId !== activation.subjectId) throw new AgentServerIdentityError("verification_failed", `authenticated whoami subject does not match agent "${activation.alias}" pinned activation`);
|
|
9637
9679
|
const identity = identityFromConfig(verified.config);
|
|
9638
9680
|
assertIdentityMatches(verified.whoami, identity, "authenticated whoami", `agent "${activation.alias}" config`);
|
|
9639
9681
|
const boundTeamId = boundTeamIdFromWhoami(verified.whoami);
|
|
9640
|
-
if (activation.boundTeamId !== boundTeamId) throw new AgentServerIdentityError("verification_failed", `authenticated whoami team binding does not match agent "${activation.alias}" pinned activation`);
|
|
9682
|
+
if (!selectAgentKeyReference(verified.config, teamId)?.teamId && activation.boundTeamId !== boundTeamId) throw new AgentServerIdentityError("verification_failed", `authenticated whoami team binding does not match agent "${activation.alias}" pinned activation`);
|
|
9641
9683
|
const refreshed = {
|
|
9642
9684
|
...activation,
|
|
9643
9685
|
...identity
|
|
@@ -9652,36 +9694,26 @@ async function verifyAgentActivation(store, alias, managedSecretProviders, exter
|
|
|
9652
9694
|
...boundTeamId ? { boundTeamId } : {}
|
|
9653
9695
|
};
|
|
9654
9696
|
}
|
|
9655
|
-
async function verifyManagedActivation(store, activation, secretProviders, connectAgent, signal) {
|
|
9697
|
+
async function verifyManagedActivation(store, activation, secretProviders, connectAgent, signal, teamId) {
|
|
9656
9698
|
const configPath = store.agentPath(activation.alias);
|
|
9657
9699
|
const config = await readCurrentConfig(configPath);
|
|
9658
9700
|
assertActivatedConfig(config, activation, configPath, requireConfigApiUrl(config, configPath), activation.apiUrl);
|
|
9659
|
-
let agentKey;
|
|
9660
|
-
try {
|
|
9661
|
-
agentKey = await resolveAgentKey(config, secretProviders);
|
|
9662
|
-
} catch (cause) {
|
|
9663
|
-
throw verificationError(`could not resolve the managed agent key for "${activation.alias}"`, cause);
|
|
9664
|
-
}
|
|
9665
|
-
if (!agentKey) throw new AgentServerIdentityError("verification_failed", `managed config for "${activation.alias}" has no agent_key_ref`);
|
|
9666
9701
|
return {
|
|
9667
9702
|
config,
|
|
9668
|
-
whoami: await
|
|
9669
|
-
agentKey,
|
|
9670
|
-
apiUrl: activation.apiUrl
|
|
9671
|
-
}, configPath, signal)
|
|
9703
|
+
whoami: await authenticateConfig(config, configPath, activation.apiUrl, secretProviders, connectAgent, signal, teamId)
|
|
9672
9704
|
};
|
|
9673
9705
|
}
|
|
9674
|
-
async function verifyExternalActivation(store, activation, secretProviders, connectAgent, signal) {
|
|
9706
|
+
async function verifyExternalActivation(store, activation, secretProviders, connectAgent, signal, teamId) {
|
|
9675
9707
|
const central = activation.configPath === store.agentPath(activation.alias);
|
|
9676
9708
|
if (!central) externalAgentLocation(activation.configPath);
|
|
9677
9709
|
assertTrustedConfigApiUrl(activation.configApiUrl);
|
|
9678
9710
|
const config = await readCurrentConfig(activation.configPath);
|
|
9679
|
-
if (central && !config
|
|
9711
|
+
if (central && !hasAgentKeyConfiguration(config)) throw new AgentServerIdentityError("unsupported_credential", `central identity "${activation.alias}" needs a stored agent key before the Agent Server can run it`);
|
|
9680
9712
|
assertActivatedConfig(config, activation, activation.configPath, requireTrustedConfigApiUrl(config, activation.configPath), activation.configApiUrl);
|
|
9681
9713
|
const effectiveApiUrl = requireTrustedApiOverride(activation.apiUrl, activation.configApiUrl, activation.configPath);
|
|
9682
9714
|
return {
|
|
9683
9715
|
config,
|
|
9684
|
-
whoami: await authenticateConfig(config, activation.configPath, effectiveApiUrl, secretProviders, connectAgent, signal)
|
|
9716
|
+
whoami: await authenticateConfig(config, activation.configPath, effectiveApiUrl, secretProviders, connectAgent, signal, teamId)
|
|
9685
9717
|
};
|
|
9686
9718
|
}
|
|
9687
9719
|
/** Never let request or activation metadata redirect persisted credentials. */
|
|
@@ -9741,18 +9773,20 @@ function externalAgentLocation(configPath) {
|
|
|
9741
9773
|
agentRoot: dirname(moltnetDir)
|
|
9742
9774
|
};
|
|
9743
9775
|
}
|
|
9744
|
-
async function authenticateConfig(config, configPath, apiUrl, secretProviders, connectAgent, signal) {
|
|
9776
|
+
async function authenticateConfig(config, configPath, apiUrl, secretProviders, connectAgent, signal, teamId) {
|
|
9745
9777
|
let agentKey;
|
|
9746
9778
|
try {
|
|
9747
|
-
agentKey = await resolveAgentKey(config, secretProviders);
|
|
9779
|
+
agentKey = await resolveAgentKey(config, secretProviders, teamId);
|
|
9748
9780
|
} catch (cause) {
|
|
9749
|
-
throw verificationError(`could not resolve the daemon agent key from ${configPath}`, cause);
|
|
9781
|
+
throw verificationError(`could not resolve the daemon agent key from ${configPath}; select a team and enroll or repair its stored key`, cause);
|
|
9750
9782
|
}
|
|
9751
9783
|
if (!agentKey) throw new AgentServerIdentityError("unsupported_credential", `external daemon config at ${configPath} must contain an agent_key_ref`);
|
|
9752
|
-
|
|
9784
|
+
const whoami = await callWhoami(connectAgent, {
|
|
9753
9785
|
agentKey,
|
|
9754
9786
|
...apiUrl ? { apiUrl } : {}
|
|
9755
9787
|
}, configPath, signal);
|
|
9788
|
+
if (!matchesCredentialTeam(whoami, selectAgentKeyReference(config, teamId)?.teamId)) throw new AgentServerIdentityError("verification_failed", "authenticated credential team binding does not match the selected team");
|
|
9789
|
+
return whoami;
|
|
9756
9790
|
}
|
|
9757
9791
|
async function callWhoami(connectAgent, options, source, signal) {
|
|
9758
9792
|
try {
|
|
@@ -9797,7 +9831,7 @@ function publicAgentView(store, activation) {
|
|
|
9797
9831
|
...activation.boundTeamId ? { teamId: activation.boundTeamId } : {},
|
|
9798
9832
|
apiUrl: activation.apiUrl,
|
|
9799
9833
|
createdAt: activation.createdAt,
|
|
9800
|
-
hasAgentKey: Boolean(config
|
|
9834
|
+
hasAgentKey: Boolean(config && hasAgentKeyConfiguration(config)),
|
|
9801
9835
|
hasPrivateKey: Boolean(config?.keys.private_key_ref)
|
|
9802
9836
|
};
|
|
9803
9837
|
}
|
|
@@ -9942,15 +9976,16 @@ var RunManager = class {
|
|
|
9942
9976
|
...target.extraArgs
|
|
9943
9977
|
];
|
|
9944
9978
|
if (activation.source === "managed") {
|
|
9945
|
-
|
|
9979
|
+
const reference = selectAgentKeyReference(config, spec.teamId)?.reference;
|
|
9980
|
+
if (!reference || !config.keys.private_key_ref) throw new AgentServerRunError("invalid_spec", `managed config for "${activation.alias}" is missing canonical secret references`);
|
|
9946
9981
|
env["MOLTNET_API_URL"] = activation.apiUrl;
|
|
9947
|
-
env["MOLTNET_AGENT_KEY_REF"] = formatSecretReferenceString(
|
|
9982
|
+
env["MOLTNET_AGENT_KEY_REF"] = formatSecretReferenceString(reference);
|
|
9948
9983
|
env["MOLTNET_PRIVATE_KEY_REF"] = formatSecretReferenceString(config.keys.private_key_ref);
|
|
9949
9984
|
env["MOLTNET_SECRET_ROOT"] = this.store.secretsDir;
|
|
9950
9985
|
} else {
|
|
9951
9986
|
env["MOLTNET_API_URL"] = activation.apiUrl ?? activation.configApiUrl;
|
|
9952
9987
|
try {
|
|
9953
|
-
const agentKey = await resolveAgentKey(config, this.options.externalSecretProviders);
|
|
9988
|
+
const agentKey = await resolveAgentKey(config, this.options.externalSecretProviders, spec.teamId);
|
|
9954
9989
|
if (!agentKey) throw new Error("external daemon config has no agent key");
|
|
9955
9990
|
env["MOLTNET_AGENT_KEY"] = agentKey;
|
|
9956
9991
|
env["MOLTNET_PRIVATE_KEY"] = await resolveIdentitySeed(config, this.options.externalSecretProviders);
|
|
@@ -9990,7 +10025,10 @@ var RunManager = class {
|
|
|
9990
10025
|
}
|
|
9991
10026
|
async startReserved(spec, signal) {
|
|
9992
10027
|
this.assertStartOpen(signal);
|
|
9993
|
-
const agent = await (this.options.verifyActivationImpl ?? verifyAgentActivation)(this.store, spec.agent, this.options.secretProviders, this.options.externalSecretProviders, void 0, signal)
|
|
10028
|
+
const agent = await (this.options.verifyActivationImpl ?? verifyAgentActivation)(this.store, spec.agent, this.options.secretProviders, this.options.externalSecretProviders, void 0, signal, spec.teamId).catch((cause) => {
|
|
10029
|
+
if (cause instanceof AgentServerIdentityError && cause.code === "verification_failed") throw new AgentServerIdentityError(cause.code, `Cannot start agent "${spec.agent}" for team "${spec.teamId}": credential verification failed. Check the selected team key and activation.`, { cause });
|
|
10030
|
+
throw cause;
|
|
10031
|
+
});
|
|
9994
10032
|
this.assertStartOpen(signal);
|
|
9995
10033
|
if (agent.boundTeamId && agent.boundTeamId !== spec.teamId) throw new AgentServerRunError("invalid_spec", `agent "${spec.agent}" has a key bound to team ${agent.boundTeamId}; start the run with that team, or create a new agent with an enrollment token from team ${spec.teamId}`);
|
|
9996
10034
|
const id = `${Date.now().toString(36)}-${randomBytes(4).toString("hex")}`;
|
|
@@ -10119,7 +10157,7 @@ var RunManager = class {
|
|
|
10119
10157
|
}
|
|
10120
10158
|
async resolveRuntimeModule(spec, activated, cwd) {
|
|
10121
10159
|
const profiles = await resolveRuntimeProfiles({
|
|
10122
|
-
agent: await this.connectActivatedAgent(activated),
|
|
10160
|
+
agent: await this.connectActivatedAgent(activated, spec.teamId),
|
|
10123
10161
|
profiles: spec.profiles,
|
|
10124
10162
|
teamId: spec.teamId,
|
|
10125
10163
|
cwd
|
|
@@ -10141,21 +10179,13 @@ var RunManager = class {
|
|
|
10141
10179
|
if (kind === "gondolin_pi") return void 0;
|
|
10142
10180
|
throw new AgentServerRunError("invalid_spec", `No local runtime is registered for profile kind "${kind}".`);
|
|
10143
10181
|
}
|
|
10144
|
-
async connectActivatedAgent(activated) {
|
|
10182
|
+
async connectActivatedAgent(activated, teamId) {
|
|
10145
10183
|
const { activation, config } = activated;
|
|
10146
|
-
|
|
10147
|
-
|
|
10148
|
-
if (!agentKey) throw new AgentServerRunError("invalid_spec", `managed agent "${activation.alias}" has no agent key`);
|
|
10149
|
-
return connect({
|
|
10150
|
-
agentKey,
|
|
10151
|
-
apiUrl: activation.apiUrl
|
|
10152
|
-
});
|
|
10153
|
-
}
|
|
10154
|
-
const agentKey = await resolveAgentKey(config, this.options.externalSecretProviders);
|
|
10155
|
-
if (!agentKey) throw new AgentServerRunError("invalid_spec", `external agent "${activation.alias}" has no agent key`);
|
|
10184
|
+
const agentKey = await resolveAgentKey(config, activation.source === "managed" ? this.options.secretProviders : this.options.externalSecretProviders, teamId);
|
|
10185
|
+
if (!agentKey) throw new AgentServerRunError("invalid_spec", `agent "${activation.alias}" has no agent key`);
|
|
10156
10186
|
return connect({
|
|
10157
10187
|
agentKey,
|
|
10158
|
-
apiUrl: activation.apiUrl ?? activation.configApiUrl
|
|
10188
|
+
apiUrl: activation.source === "managed" ? activation.apiUrl : activation.apiUrl ?? activation.configApiUrl
|
|
10159
10189
|
});
|
|
10160
10190
|
}
|
|
10161
10191
|
stop(id) {
|
|
@@ -10638,7 +10668,8 @@ var CreateAgentSchema = Type.Union([Type.Object({
|
|
|
10638
10668
|
enrollmentToken: Type.String()
|
|
10639
10669
|
}), Type.Object({
|
|
10640
10670
|
kind: Type.Literal("external"),
|
|
10641
|
-
identityAlias: Type.String()
|
|
10671
|
+
identityAlias: Type.String(),
|
|
10672
|
+
teamId: Type.Optional(Type.String({ format: "uuid" }))
|
|
10642
10673
|
})]);
|
|
10643
10674
|
var ReconcileAgentSchema = Type.Object({ action: Type.Union([Type.Literal("resume"), Type.Literal("abandon")]) });
|
|
10644
10675
|
var ReconcileAgentResultSchema = Type.Union([schemaRef(AgentServerAgentSchema), Type.Object({ abandoned: Type.Literal(true) })], { $id: "ReconcileAgentResult" });
|
|
@@ -11151,6 +11182,7 @@ function registerAgentRoutes(app, options, requirePairedOrigin) {
|
|
|
11151
11182
|
const entry = await attachExternalAgent(store, options.externalSecretProviders, {
|
|
11152
11183
|
name: identityAlias,
|
|
11153
11184
|
configDir: store.identityDir(identityAlias),
|
|
11185
|
+
...typeof body["teamId"] === "string" ? { teamId: body["teamId"] } : {},
|
|
11154
11186
|
signal
|
|
11155
11187
|
});
|
|
11156
11188
|
return reply.code(201).send(publicAgentView(store, entry.activation));
|
|
@@ -11182,7 +11214,7 @@ function identityViews(store) {
|
|
|
11182
11214
|
return store.listIdentityAliases().map((alias) => ({
|
|
11183
11215
|
alias,
|
|
11184
11216
|
activated: activated.has(alias),
|
|
11185
|
-
hasAgentKey:
|
|
11217
|
+
hasAgentKey: hasAgentKeyConfiguration(store.readAgentConfig(alias) ?? {})
|
|
11186
11218
|
}));
|
|
11187
11219
|
}
|
|
11188
11220
|
function registerProviderRoutes(app, options, requirePairedOrigin) {
|
|
@@ -12020,11 +12052,13 @@ async function runSyncSessions(argv) {
|
|
|
12020
12052
|
const ctx = await resolveAgentContext(identity.agent, {
|
|
12021
12053
|
credentialSource: cfg.credentialSource,
|
|
12022
12054
|
envApiUrl: cfg.apiUrl,
|
|
12055
|
+
teamId: values.team,
|
|
12023
12056
|
agentRootDir: explicitAgentRootDir
|
|
12024
12057
|
});
|
|
12025
12058
|
await validateStartupBinding({
|
|
12026
12059
|
agent: ctx.agent,
|
|
12027
|
-
teamId: values.team
|
|
12060
|
+
teamId: values.team,
|
|
12061
|
+
credentialTeamId: ctx.credentialTeamId
|
|
12028
12062
|
});
|
|
12029
12063
|
const stateDirs = ensureDaemonStateDirs(agentRootDir);
|
|
12030
12064
|
const result = await syncRuntimeSessions({
|
|
@@ -12209,7 +12243,7 @@ async function writeCache(cache) {
|
|
|
12209
12243
|
}
|
|
12210
12244
|
//#endregion
|
|
12211
12245
|
//#region src/version.ts
|
|
12212
|
-
var DAEMON_VERSION = "0.
|
|
12246
|
+
var DAEMON_VERSION = "0.60.0";
|
|
12213
12247
|
//#endregion
|
|
12214
12248
|
//#region src/cli.ts
|
|
12215
12249
|
async function runAgentDaemonCli(options) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/agent-daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.60.0",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Universal MoltNet agent daemon host with a built-in Pi/Gondolin runtime and support for trusted operator-owned runtime modules. CLI: moltnet-agent.",
|
|
@@ -85,10 +85,10 @@
|
|
|
85
85
|
"proper-lockfile": "4.1.2",
|
|
86
86
|
"reflect-metadata": "^0.2.2",
|
|
87
87
|
"typebox": "^1.2.8",
|
|
88
|
-
"@themoltnet/agent-runtime": "1.0
|
|
89
|
-
"@themoltnet/pi-runtime": "0.18.0",
|
|
88
|
+
"@themoltnet/agent-runtime": "1.1.0",
|
|
90
89
|
"@themoltnet/os-keyring": "0.3.0",
|
|
91
|
-
"@themoltnet/sdk": "0.
|
|
90
|
+
"@themoltnet/sdk": "0.143.0",
|
|
91
|
+
"@themoltnet/pi-runtime": "0.18.1"
|
|
92
92
|
},
|
|
93
93
|
"devDependencies": {
|
|
94
94
|
"@fastify/swagger": "^9.6.1",
|
|
@@ -98,17 +98,17 @@
|
|
|
98
98
|
"vite": "^8.0.0",
|
|
99
99
|
"vite-plugin-dts": "^4.5.4",
|
|
100
100
|
"vitest": "^3.0.0",
|
|
101
|
+
"@moltnet/api-client": "0.1.0",
|
|
101
102
|
"@moltnet/agent-eval": "0.1.0",
|
|
102
103
|
"@moltnet/bootstrap": "0.1.0",
|
|
103
104
|
"@moltnet/crypto-service": "0.1.0",
|
|
104
|
-
"@moltnet/api-client": "0.1.0",
|
|
105
105
|
"@moltnet/execution-integrations": "0.1.0",
|
|
106
106
|
"@moltnet/execution-plan": "0.1.0",
|
|
107
107
|
"@moltnet/loopback-companion": "0.1.0",
|
|
108
|
-
"@moltnet/observability": "0.1.0",
|
|
109
108
|
"@moltnet/runtime-profiles": "0.1.0",
|
|
110
|
-
"@moltnet/
|
|
111
|
-
"@moltnet/tasks": "0.1.0"
|
|
109
|
+
"@moltnet/observability": "0.1.0",
|
|
110
|
+
"@moltnet/tasks": "0.1.0",
|
|
111
|
+
"@moltnet/models": "0.1.0"
|
|
112
112
|
},
|
|
113
113
|
"nx": {
|
|
114
114
|
"projectType": "application",
|