@cotal-ai/manager 0.46.0 → 0.47.1
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/dist/commands.d.ts +1 -0
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +9 -9
- package/dist/commands.js.map +1 -1
- package/dist/manager-service-contract.d.ts +6 -2
- package/dist/manager-service-contract.d.ts.map +1 -1
- package/dist/manager-service-contract.js +7 -3
- package/dist/manager-service-contract.js.map +1 -1
- package/dist/manager.d.ts +8 -0
- package/dist/manager.d.ts.map +1 -1
- package/dist/manager.js +33 -23
- package/dist/manager.js.map +1 -1
- package/dist/remote-register.d.ts +4 -0
- package/dist/remote-register.d.ts.map +1 -1
- package/dist/remote-register.js +7 -5
- package/dist/remote-register.js.map +1 -1
- package/dist/run-hosting.d.ts.map +1 -1
- package/dist/run-hosting.js +4 -4
- package/dist/run-hosting.js.map +1 -1
- package/dist/runtime/custodial-pty.d.ts +16 -0
- package/dist/runtime/custodial-pty.d.ts.map +1 -0
- package/dist/runtime/custodial-pty.js +40 -0
- package/dist/runtime/custodial-pty.js.map +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +5 -2
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/pty.d.ts +10 -6
- package/dist/runtime/pty.d.ts.map +1 -1
- package/dist/runtime/pty.js +12 -5
- package/dist/runtime/pty.js.map +1 -1
- package/package.json +10 -9
package/dist/manager.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import { createHash, randomUUID, randomBytes } from "node:crypto";
|
|
3
3
|
import { hostname } from "node:os";
|
|
4
|
-
import {
|
|
4
|
+
import { credsAuthenticator } from "@nats-io/transport-node";
|
|
5
5
|
import { existsSync, lstatSync, readFileSync, rmSync } from "node:fs";
|
|
6
6
|
import { join, dirname, resolve } from "node:path";
|
|
7
|
-
import { CotalEndpoint, DEFAULT_SERVER, DEV_OWNER, MANAGER_LEASE_RENEW_MS, STANDING_RENEWABLE_TTL_SEC, agentFilePath, clearSpaceHistory, connectorServers, spawnEnvAllow, deprovisionAgent, firstFreeName, spawnNameError, idFromCreds, inspectCredHealth, loadAgentFile, listPersonaCatalog, personaCatalogDescription, personaCatalogReadable, loadCotalConfig, mintCreds, mintLifecycleUid, mkSecretDir, newIdentity, actionContext, parsePrincipalKey, parseShareSelection, principalKey, probeConnect, provisionAgent, provisionAgentDurables, registry, resolveAuthProvider, saveAgentFile, subjectMatches, AUTH_ENDPOINT, EP_CMD_RETIRE_LIFECYCLE, epRequestSubject, epCallerReplyFilter, parseEpSubject, controlServiceSubject, eventChannelPrincipal, } from "@cotal-ai/core";
|
|
7
|
+
import { CotalEndpoint, DEFAULT_SERVER, DEV_OWNER, MANAGER_LEASE_RENEW_MS, STANDING_RENEWABLE_TTL_SEC, agentFilePath, clearSpaceHistory, dialerFor, connectorServers, spawnEnvAllow, deprovisionAgent, firstFreeName, spawnNameError, idFromCreds, inspectCredHealth, loadAgentFile, listPersonaCatalog, personaCatalogDescription, personaCatalogReadable, loadCotalConfig, mintCreds, mintLifecycleUid, mkSecretDir, newIdentity, actionContext, parsePrincipalKey, parseShareSelection, principalKey, probeConnect, provisionAgent, provisionAgentDurables, registry, resolveAuthProvider, saveAgentFile, subjectMatches, AUTH_ENDPOINT, EP_CMD_RETIRE_LIFECYCLE, epRequestSubject, epCallerReplyFilter, parseEpSubject, controlServiceSubject, eventChannelPrincipal, } from "@cotal-ai/core";
|
|
8
8
|
import { agentAuthState, agentCredsDir, agentLifecycleSecretFilePaths, agentSecretFilePaths, agentSecretKeyForFile, authDir, connectorInstallHint, DEFAULT_CONNECTOR, defaultAgentType, DELIVERY_CREDS_KIND, extensionConnectors, findCotalRoot, getSpaceAuth, hasUserAuthState, loadExtensionsManifest, loadManagerInstanceIdentity, loadMeshes, manifestExtensionNames, materializeFromManifest, materializeSecretToFile, MEMBERSHIP_RW_CREDS_KIND, mergeLaunchOptions, remintDaemonCreds, resolveOnPath, saveManagerInstanceIdentity, spaceMaterialKey, SYSTEM_CREDS_FILES, userAuthStateDir, workspaceSecretStore, writeRenewalRecord } from "@cotal-ai/workspace";
|
|
9
9
|
import { createRuntime, requireRuntimeAdopt, } from "./runtime/index.js";
|
|
10
10
|
import { AttachEndpoint } from "./attach-endpoint.js";
|
|
@@ -462,6 +462,17 @@ export class Manager {
|
|
|
462
462
|
resumeDurableCommitToken;
|
|
463
463
|
resumedAgentNames = new Set();
|
|
464
464
|
remoteAuthority;
|
|
465
|
+
/** Every broker dial this manager makes, through ONE transport decision.
|
|
466
|
+
*
|
|
467
|
+
* `this.servers` is whatever the mesh record held (`cotal supervise` reads it from there), so it
|
|
468
|
+
* can be a `ws://`/`wss://` broker published through an HTTPS edge. The raw node transport
|
|
469
|
+
* refuses such a URL instead of dialing it, so the scheme picks the dialer here rather than at
|
|
470
|
+
* thirteen call sites. `dialerFor` also owns the websocket transport's options, which is why
|
|
471
|
+
* callers below still pass their own auth options unchanged. */
|
|
472
|
+
dial(opts) {
|
|
473
|
+
const servers = this.servers ?? DEFAULT_SERVER;
|
|
474
|
+
return dialerFor(servers)({ ...opts, servers });
|
|
475
|
+
}
|
|
465
476
|
constructor(opts) {
|
|
466
477
|
this.space = opts.space;
|
|
467
478
|
this.servers = opts.servers;
|
|
@@ -1154,8 +1165,6 @@ export class Manager {
|
|
|
1154
1165
|
async awaitHandleExit(handle) {
|
|
1155
1166
|
if (!handle.waitForExit)
|
|
1156
1167
|
throw new Error(`runtime "${handle.kind}" cannot prove child exit (AgentHandle.waitForExit is not implemented)`);
|
|
1157
|
-
if (handle.status() === "exited")
|
|
1158
|
-
return;
|
|
1159
1168
|
await withTimeout(handle.waitForExit(), this.preserveStopTimeoutMs, `child did not exit within ${this.preserveStopTimeoutMs}ms`);
|
|
1160
1169
|
if (handle.status() !== "exited")
|
|
1161
1170
|
throw new Error(`runtime "${handle.kind}" reported exit completion but status is still running`);
|
|
@@ -2540,7 +2549,7 @@ export class Manager {
|
|
|
2540
2549
|
const creds = await mintCreds(this.auth, newIdentity(), "retirement-requester", {
|
|
2541
2550
|
retirementRequester: { ...caller, target: { owner: target.owner, actor: target.actor, lifecycleUid: a.lifecycleUid } },
|
|
2542
2551
|
});
|
|
2543
|
-
const nc = await
|
|
2552
|
+
const nc = await this.dial({ authenticator: credsAuthenticator(new TextEncoder().encode(creds)), maxReconnectAttempts: 0 });
|
|
2544
2553
|
try {
|
|
2545
2554
|
// §13.2 nonce: >=128 bits of CSPRNG entropy, base64url (the `endpoint-invoke` idiom).
|
|
2546
2555
|
const nonce = randomBytes(24).toString("base64url");
|
|
@@ -4444,7 +4453,12 @@ export class Manager {
|
|
|
4444
4453
|
// └──────────────────────────────────────────────────────────────────────────────────────┘
|
|
4445
4454
|
const deliberate = a.terminalizing === true;
|
|
4446
4455
|
void (async () => {
|
|
4447
|
-
|
|
4456
|
+
// waitForExit may close the attach stream before this snapshot
|
|
4457
|
+
let tail = "";
|
|
4458
|
+
try {
|
|
4459
|
+
tail = this.tail(await s.backlog());
|
|
4460
|
+
}
|
|
4461
|
+
catch { }
|
|
4448
4462
|
if (opts.reapOnExit !== false)
|
|
4449
4463
|
this.onAgentExit(a);
|
|
4450
4464
|
// A DELIBERATE STOP IS NOT A LAUNCH FAILURE. The despawn path owns this goal's terminal
|
|
@@ -4569,7 +4583,7 @@ export class Manager {
|
|
|
4569
4583
|
const creds = await mintCreds(this.auth, identity, "lifecycle-executor", {
|
|
4570
4584
|
lifecycleExecutor: { owner: pin.owner, actor: pin.actor, lifecycleUid: pin.lifecycleUid, alias: pin.alias },
|
|
4571
4585
|
});
|
|
4572
|
-
const nc = await
|
|
4586
|
+
const nc = await this.dial({ ...standaloneConnectOpts({ creds, /* not yet wired to a recorded transport */ tls: false }), maxReconnectAttempts: 0 });
|
|
4573
4587
|
try {
|
|
4574
4588
|
const kvm = new Kvm(nc);
|
|
4575
4589
|
const recordsKv = await kvm.open(recordsBucket(this.space));
|
|
@@ -4594,7 +4608,7 @@ export class Manager {
|
|
|
4594
4608
|
: undefined);
|
|
4595
4609
|
if (!creds)
|
|
4596
4610
|
throw new Error("withEndpointServeExecutor: no scoped executor authority (an open mesh must use the bare path)");
|
|
4597
|
-
const nc = await
|
|
4611
|
+
const nc = await this.dial({ ...standaloneConnectOpts({ creds, /* not yet wired to a recorded transport */ tls: false }), maxReconnectAttempts: 0 });
|
|
4598
4612
|
try {
|
|
4599
4613
|
const kvm = new Kvm(nc);
|
|
4600
4614
|
return await fn({ recordsKv: await kvm.open(recordsBucket(this.space)), authKv: await kvm.open(epAuthBucket(this.space)), nc });
|
|
@@ -4610,7 +4624,7 @@ export class Manager {
|
|
|
4610
4624
|
async withOpenServeConnection(fn) {
|
|
4611
4625
|
if (this.auth)
|
|
4612
4626
|
throw new Error("withOpenServeConnection: an auth mesh must use the scoped endpoint-serve executor");
|
|
4613
|
-
const nc = await
|
|
4627
|
+
const nc = await this.dial({ maxReconnectAttempts: 0 });
|
|
4614
4628
|
try {
|
|
4615
4629
|
const kvm = new Kvm(nc);
|
|
4616
4630
|
// An open mesh may be a RAW broker (no `cotal up` provisioning ran), and `Kvm.open` binds
|
|
@@ -4629,7 +4643,7 @@ export class Manager {
|
|
|
4629
4643
|
return {
|
|
4630
4644
|
instanceId: this.managerInstanceId,
|
|
4631
4645
|
runtime: this.runtime.kind,
|
|
4632
|
-
custody: "legacy",
|
|
4646
|
+
custody: process.platform === "linux" && this.runtime.kind === "pty" ? "custodied" : "legacy",
|
|
4633
4647
|
agentCount: this.agents.size,
|
|
4634
4648
|
uptimeMs: Date.now() - this.startedAtMs,
|
|
4635
4649
|
connectors: this.connectorStatuses.map((row) => ({ ...row, binaries: { ...row.binaries } })),
|
|
@@ -4710,8 +4724,7 @@ export class Manager {
|
|
|
4710
4724
|
creds: remote.serveCreds,
|
|
4711
4725
|
};
|
|
4712
4726
|
const enc = new TextEncoder();
|
|
4713
|
-
const nc = await
|
|
4714
|
-
servers: this.servers ?? DEFAULT_SERVER,
|
|
4727
|
+
const nc = await this.dial({
|
|
4715
4728
|
authenticator: (nonce) => credsAuthenticator(enc.encode(state.creds))(nonce),
|
|
4716
4729
|
inboxPrefix: `_INBOX_${state.identity.id}`,
|
|
4717
4730
|
maxReconnectAttempts: -1,
|
|
@@ -4744,7 +4757,7 @@ export class Manager {
|
|
|
4744
4757
|
{
|
|
4745
4758
|
// Open mesh: the bare connection holds the rights (there is no credential system to mint from).
|
|
4746
4759
|
const provCreds = auth ? await mintCreds(auth, newIdentity(), "provisioner") : undefined;
|
|
4747
|
-
const provNc = await
|
|
4760
|
+
const provNc = await this.dial({ ...standaloneConnectOpts({ creds: provCreds, /* not yet wired to a recorded transport */ tls: false }), maxReconnectAttempts: 0 });
|
|
4748
4761
|
try {
|
|
4749
4762
|
// P2 item 2: the manager now WRITES goal facts (EPF) + progress events (EPE), so the §13.12
|
|
4750
4763
|
// endpoint streams must exist. Nothing provisioned them before spawn-as-action (no endpoint
|
|
@@ -4889,8 +4902,7 @@ export class Manager {
|
|
|
4889
4902
|
// registered surface for its whole incarnation.
|
|
4890
4903
|
const state = { handle: undefined, nc: undefined, identity: serveIdentity, grant, creds };
|
|
4891
4904
|
const enc = new TextEncoder();
|
|
4892
|
-
const nc = await
|
|
4893
|
-
servers: this.servers ?? DEFAULT_SERVER,
|
|
4905
|
+
const nc = await this.dial({
|
|
4894
4906
|
// Open mesh: a bare serve connection (no credential exists; the broker enforces nothing).
|
|
4895
4907
|
...(creds !== undefined ? { authenticator: (nonce) => credsAuthenticator(enc.encode(state.creds))(nonce) } : {}),
|
|
4896
4908
|
inboxPrefix: `_INBOX_${serveIdentity.id}`,
|
|
@@ -4985,8 +4997,7 @@ export class Manager {
|
|
|
4985
4997
|
// The mutable holder captured by the authenticator (mirrors the serve connection): a half-TTL
|
|
4986
4998
|
// renewal updates `gw.creds` and the next (re)connect presents the refreshed credential.
|
|
4987
4999
|
const gw = { nc: undefined, ctx: undefined, creds: (this.auth || this.remoteAuthority) ? this.goalWriterCreds : undefined, identity };
|
|
4988
|
-
const nc = await
|
|
4989
|
-
servers: this.servers ?? DEFAULT_SERVER,
|
|
5000
|
+
const nc = await this.dial({
|
|
4990
5001
|
...((this.auth || this.remoteAuthority) ? { authenticator: (nonce) => credsAuthenticator(enc.encode(gw.creds))(nonce) } : {}),
|
|
4991
5002
|
inboxPrefix: `_INBOX_${identity.id}`,
|
|
4992
5003
|
maxReconnectAttempts: -1,
|
|
@@ -5091,8 +5102,7 @@ export class Manager {
|
|
|
5091
5102
|
// The mutable holder captured by the authenticator (mirrors the goal-writer): a half-TTL renewal
|
|
5092
5103
|
// updates `sw.creds` and the next (re)connect presents the refreshed credential.
|
|
5093
5104
|
const sw = { nc: undefined, creds: (this.auth || this.remoteAuthority) ? this.sessionLedgerCreds : undefined };
|
|
5094
|
-
const nc = await
|
|
5095
|
-
servers: this.servers ?? DEFAULT_SERVER,
|
|
5105
|
+
const nc = await this.dial({
|
|
5096
5106
|
...((this.auth || this.remoteAuthority) ? { authenticator: (nonce) => credsAuthenticator(enc.encode(sw.creds))(nonce) } : {}),
|
|
5097
5107
|
inboxPrefix: `_INBOX_${identity.id}`,
|
|
5098
5108
|
maxReconnectAttempts: -1,
|
|
@@ -5263,7 +5273,7 @@ export class Manager {
|
|
|
5263
5273
|
// FAIL LOUD: there is deliberately no shared connection to fall back to. Serving a session
|
|
5264
5274
|
// without its own credential is exactly the standing-writer shape this design removes.
|
|
5265
5275
|
const opts = (this.auth || this.remoteAuthority) ? standaloneConnectOpts({ creds: cred.creds, /* not yet wired to a recorded transport */ tls: false }) : {};
|
|
5266
|
-
return
|
|
5276
|
+
return this.dial({ ...opts, maxReconnectAttempts: -1 });
|
|
5267
5277
|
},
|
|
5268
5278
|
revoke: async (credentialId) => {
|
|
5269
5279
|
if (!this.auth && !this.remoteAuthority)
|
|
@@ -5318,8 +5328,8 @@ export class Manager {
|
|
|
5318
5328
|
try {
|
|
5319
5329
|
let entries = [];
|
|
5320
5330
|
const nc = this.auth
|
|
5321
|
-
? await
|
|
5322
|
-
: await
|
|
5331
|
+
? await this.dial({ ...standaloneConnectOpts({ creds: await mintCreds(this.auth, newIdentity(), "provisioner"), /* not yet wired to a recorded transport */ tls: false }), maxReconnectAttempts: 0 })
|
|
5332
|
+
: await this.dial({ maxReconnectAttempts: 0 });
|
|
5323
5333
|
try {
|
|
5324
5334
|
const kvm = new Kvm(nc);
|
|
5325
5335
|
await ensureAuthorityStores(await jetstreamManager(nc), kvm, this.space);
|
|
@@ -6281,7 +6291,7 @@ export class Manager {
|
|
|
6281
6291
|
return;
|
|
6282
6292
|
const identity = newIdentity();
|
|
6283
6293
|
const creds = await mintCreds(this.auth, identity, "provisioner");
|
|
6284
|
-
const nc = await
|
|
6294
|
+
const nc = await this.dial({ ...standaloneConnectOpts({ creds, /* not yet wired to a recorded transport */ tls: false }), maxReconnectAttempts: 0 });
|
|
6285
6295
|
const slotRows = [];
|
|
6286
6296
|
try {
|
|
6287
6297
|
const jsm = await jetstreamManager(nc);
|