agent-relay 11.4.0 → 11.4.2
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 +73 -0
- package/dist/cli/agent-relay-mcp.d.ts.map +1 -1
- package/dist/cli/agent-relay-mcp.js +28 -12
- package/dist/cli/agent-relay-mcp.js.map +1 -1
- package/dist/cli/bootstrap.d.ts.map +1 -1
- package/dist/cli/bootstrap.js +5 -2
- package/dist/cli/bootstrap.js.map +1 -1
- package/dist/cli/commands/cloud.d.ts +4 -0
- package/dist/cli/commands/cloud.d.ts.map +1 -1
- package/dist/cli/commands/cloud.js +45 -3
- package/dist/cli/commands/cloud.js.map +1 -1
- package/dist/cli/commands/core.d.ts +5 -0
- package/dist/cli/commands/core.d.ts.map +1 -1
- package/dist/cli/commands/core.js +6 -1
- package/dist/cli/commands/core.js.map +1 -1
- package/dist/cli/commands/node.d.ts +3 -1
- package/dist/cli/commands/node.d.ts.map +1 -1
- package/dist/cli/commands/node.js +75 -12
- package/dist/cli/commands/node.js.map +1 -1
- package/dist/cli/commands/workspace.d.ts.map +1 -1
- package/dist/cli/commands/workspace.js +72 -6
- package/dist/cli/commands/workspace.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +6 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/lib/broker-lifecycle.d.ts +7 -0
- package/dist/cli/lib/broker-lifecycle.d.ts.map +1 -1
- package/dist/cli/lib/broker-lifecycle.js +203 -39
- package/dist/cli/lib/broker-lifecycle.js.map +1 -1
- package/dist/cli/lib/enrollment-pin.d.ts +50 -0
- package/dist/cli/lib/enrollment-pin.d.ts.map +1 -0
- package/dist/cli/lib/enrollment-pin.js +38 -0
- package/dist/cli/lib/enrollment-pin.js.map +1 -0
- package/dist/cli/lib/exit.d.ts.map +1 -1
- package/dist/cli/lib/exit.js +3 -2
- package/dist/cli/lib/exit.js.map +1 -1
- package/dist/cli/lib/flush-stdio.d.ts +52 -0
- package/dist/cli/lib/flush-stdio.d.ts.map +1 -0
- package/dist/cli/lib/flush-stdio.js +68 -0
- package/dist/cli/lib/flush-stdio.js.map +1 -0
- package/dist/cli/lib/project-workspace-key.d.ts +1 -1
- package/dist/cli/lib/project-workspace-key.d.ts.map +1 -1
- package/dist/cli/lib/project-workspace-key.js +1 -1
- package/dist/cli/lib/project-workspace-key.js.map +1 -1
- package/dist/cli/lib/workspace-session.d.ts +29 -1
- package/dist/cli/lib/workspace-session.d.ts.map +1 -1
- package/dist/cli/lib/workspace-session.js +57 -2
- package/dist/cli/lib/workspace-session.js.map +1 -1
- package/dist/cli/mcp/action-tools.d.ts.map +1 -1
- package/dist/cli/mcp/action-tools.js +2 -1
- package/dist/cli/mcp/action-tools.js.map +1 -1
- package/dist/cli/mcp/messaging-tools.d.ts.map +1 -1
- package/dist/cli/mcp/messaging-tools.js +40 -20
- package/dist/cli/mcp/messaging-tools.js.map +1 -1
- package/dist/cli/telemetry/client.d.ts.map +1 -1
- package/dist/cli/telemetry/client.js +7 -5
- package/dist/cli/telemetry/client.js.map +1 -1
- package/dist/cli/telemetry/events.d.ts +2 -0
- package/dist/cli/telemetry/events.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/package.json +9 -9
|
@@ -3,7 +3,17 @@ import { resolveActiveWorkspace } from '@agent-relay/cloud';
|
|
|
3
3
|
import { maskSecret } from '../lib/redact.js';
|
|
4
4
|
import { printJson, runSdk, withSdkDefaults } from '../lib/sdk-command.js';
|
|
5
5
|
import { readWorkspaceStore, setWorkspaceKey } from '../lib/workspace-store.js';
|
|
6
|
-
import { persistWorkspaceSession, validateWorkspaceSessionName } from '../lib/workspace-session.js';
|
|
6
|
+
import { describeClearedEnrollment, persistWorkspaceSession, pinProjectWorkspaceSession, validateWorkspaceSessionName, } from '../lib/workspace-session.js';
|
|
7
|
+
/**
|
|
8
|
+
* Say so when moving workspaces drops this project's enrolled fleet node.
|
|
9
|
+
* Leaving it silent is what produced the split rosters in #1432.
|
|
10
|
+
*/
|
|
11
|
+
function reportClearedEnrollment(result, deps) {
|
|
12
|
+
const message = describeClearedEnrollment(result);
|
|
13
|
+
if (message) {
|
|
14
|
+
deps.log(message);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
7
17
|
function parsePositiveInteger(value) {
|
|
8
18
|
const parsed = Number.parseInt(value, 10);
|
|
9
19
|
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
@@ -56,14 +66,27 @@ export function registerWorkspaceCommands(program, overrides = {}) {
|
|
|
56
66
|
await runSdk(deps, async () => {
|
|
57
67
|
const workspaceName = validateWorkspaceSessionName(name);
|
|
58
68
|
const relay = await deps.createWorkspace(workspaceName, o.baseUrl);
|
|
59
|
-
|
|
60
|
-
|
|
69
|
+
const previousActive = readWorkspaceStore().active;
|
|
70
|
+
const persisted = relay.workspaceKey
|
|
71
|
+
? persistWorkspaceSession({ name: workspaceName, workspaceKey: relay.workspaceKey })
|
|
72
|
+
: {};
|
|
73
|
+
if (relay.workspaceKey && previousActive && previousActive !== workspaceName) {
|
|
74
|
+
deps.error(`⚠ Active workspace changed: ${previousActive} → ${workspaceName}`);
|
|
75
|
+
deps.error(' Restore with: agent-relay workspace restore');
|
|
61
76
|
}
|
|
62
77
|
// The key is persisted to the workspace store either way; the output
|
|
63
|
-
// masks it unless the caller explicitly asks for the raw value.
|
|
78
|
+
// masks it unless the caller explicitly asks for the raw value. A
|
|
79
|
+
// dropped enrollment rides in the JSON rather than a log line so the
|
|
80
|
+
// output stays parseable.
|
|
64
81
|
printJson(deps, {
|
|
65
82
|
name: workspaceName,
|
|
66
83
|
workspaceKey: relay.workspaceKey && !o.revealSecrets ? maskSecret(relay.workspaceKey) : relay.workspaceKey,
|
|
84
|
+
...(persisted.clearedEnrolledNodeId
|
|
85
|
+
? {
|
|
86
|
+
clearedEnrolledNodeId: persisted.clearedEnrolledNodeId,
|
|
87
|
+
warning: describeClearedEnrollment(persisted),
|
|
88
|
+
}
|
|
89
|
+
: {}),
|
|
67
90
|
});
|
|
68
91
|
});
|
|
69
92
|
});
|
|
@@ -75,6 +98,7 @@ export function registerWorkspaceCommands(program, overrides = {}) {
|
|
|
75
98
|
const store = readWorkspaceStore();
|
|
76
99
|
printJson(deps, {
|
|
77
100
|
active: store.active,
|
|
101
|
+
previous: store.previous,
|
|
78
102
|
workspaces: Object.keys(store.workspaces),
|
|
79
103
|
});
|
|
80
104
|
});
|
|
@@ -116,8 +140,9 @@ export function registerWorkspaceCommands(program, overrides = {}) {
|
|
|
116
140
|
.argument('<key>', 'Workspace key')
|
|
117
141
|
.action(async (name, key) => {
|
|
118
142
|
await runSdk(deps, async () => {
|
|
119
|
-
persistWorkspaceSession({ name, workspaceKey: key });
|
|
143
|
+
const result = persistWorkspaceSession({ name, workspaceKey: key });
|
|
120
144
|
deps.log(`Joined and switched to workspace "${name}".`);
|
|
145
|
+
reportClearedEnrollment(result, deps);
|
|
121
146
|
});
|
|
122
147
|
});
|
|
123
148
|
group
|
|
@@ -131,8 +156,49 @@ export function registerWorkspaceCommands(program, overrides = {}) {
|
|
|
131
156
|
if (!workspace) {
|
|
132
157
|
throw new Error(`Unknown workspace "${name}". Add it with \`relay workspace set_key ${name} <key>\`.`);
|
|
133
158
|
}
|
|
134
|
-
persistWorkspaceSession({ name, workspaceKey: workspace.key });
|
|
159
|
+
const result = persistWorkspaceSession({ name, workspaceKey: workspace.key });
|
|
135
160
|
deps.log(`Switched to workspace "${name}".`);
|
|
161
|
+
reportClearedEnrollment(result, deps);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
group
|
|
165
|
+
.command('restore')
|
|
166
|
+
.description('Switch back to the previously active workspace')
|
|
167
|
+
.action(async () => {
|
|
168
|
+
await runSdk(deps, async () => {
|
|
169
|
+
const store = readWorkspaceStore();
|
|
170
|
+
const previous = store.previous;
|
|
171
|
+
if (!previous) {
|
|
172
|
+
throw new Error('No previous workspace is recorded.');
|
|
173
|
+
}
|
|
174
|
+
if (previous === store.active) {
|
|
175
|
+
throw new Error(`The recorded previous workspace "${previous}" is already active.`);
|
|
176
|
+
}
|
|
177
|
+
const workspace = Object.hasOwn(store.workspaces, previous) ? store.workspaces[previous] : undefined;
|
|
178
|
+
if (!workspace) {
|
|
179
|
+
throw new Error(`The recorded previous workspace "${previous}" no longer exists.`);
|
|
180
|
+
}
|
|
181
|
+
const current = store.active;
|
|
182
|
+
persistWorkspaceSession({ name: previous, workspaceKey: workspace.key });
|
|
183
|
+
deps.log(`Switched to workspace "${previous}" (was ${current ?? 'none'}).`);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
group
|
|
187
|
+
.command('rebind')
|
|
188
|
+
.description("Pin this project's broker to a stored workspace")
|
|
189
|
+
.argument('<name>', 'Stored workspace name')
|
|
190
|
+
.action(async (name) => {
|
|
191
|
+
await runSdk(deps, async () => {
|
|
192
|
+
const workspaceName = validateWorkspaceSessionName(name);
|
|
193
|
+
const store = readWorkspaceStore();
|
|
194
|
+
const workspace = Object.hasOwn(store.workspaces, workspaceName)
|
|
195
|
+
? store.workspaces[workspaceName]
|
|
196
|
+
: undefined;
|
|
197
|
+
if (!workspace) {
|
|
198
|
+
throw new Error(`Unknown workspace "${workspaceName}". Add it with \`relay workspace set_key ${workspaceName} <key>\`.`);
|
|
199
|
+
}
|
|
200
|
+
pinProjectWorkspaceSession({ workspaceKey: workspace.key });
|
|
201
|
+
deps.log(`Rebound this project's broker to workspace "${workspaceName}". Restart the broker to apply it.`);
|
|
136
202
|
});
|
|
137
203
|
});
|
|
138
204
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../src/cli/commands/workspace.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,EAAuB,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,
|
|
1
|
+
{"version":3,"file":"workspace.js","sourceRoot":"","sources":["../../../src/cli/commands/workspace.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAYA,cAAc,gBAAgB,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
5
5
|
import { runAiSdkSidecarMain } from '@agent-relay/harnesses';
|
|
6
6
|
import { runCli } from './bootstrap.js';
|
|
7
7
|
import { describeError } from './lib/describe-error.js';
|
|
8
|
+
import { exitAfterFlush } from './lib/flush-stdio.js';
|
|
8
9
|
export * from './bootstrap.js';
|
|
9
10
|
function isEntrypoint() {
|
|
10
11
|
const invocationPath = process.argv[1];
|
|
@@ -19,13 +20,16 @@ function isEntrypoint() {
|
|
|
19
20
|
}
|
|
20
21
|
if (isEntrypoint()) {
|
|
21
22
|
const main = process.argv[2] === '__ai-sdk-sidecar' ? runAiSdkSidecarMain(process.argv.slice(3)) : runCli();
|
|
22
|
-
main.catch((err) => {
|
|
23
|
+
main.catch(async (err) => {
|
|
23
24
|
// Commander will have already printed a helpful message for parse errors.
|
|
24
25
|
// For other top-level failures, surface them to stderr and exit non-zero.
|
|
25
26
|
// `describeError` keeps a non-Error rejection (e.g. `{ status: 401 }` from
|
|
26
27
|
// a broker client) readable instead of collapsing it to `[object Object]`.
|
|
27
28
|
process.stderr.write(`${describeError(err)}\n`);
|
|
28
|
-
|
|
29
|
+
// Flush first: on macOS a piped stderr is asynchronous, so exiting in this
|
|
30
|
+
// tick would discard the message we just wrote and leave the caller with a
|
|
31
|
+
// silent non-zero exit.
|
|
32
|
+
await exitAfterFlush(1);
|
|
29
33
|
});
|
|
30
34
|
}
|
|
31
35
|
//# sourceMappingURL=index.js.map
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,cAAc,gBAAgB,CAAC;AAE/B,SAAS,YAAY;IACnB,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvC,IAAI,CAAC,cAAc;QAAE,OAAO,KAAK,CAAC;IAClC,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7F,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED,IAAI,YAAY,EAAE,EAAE,CAAC;IACnB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,kBAAkB,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAC5G,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACvB,0EAA0E;QAC1E,0EAA0E;QAC1E,2EAA2E;QAC3E,2EAA2E;QAC3E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChD,2EAA2E;QAC3E,2EAA2E;QAC3E,wBAAwB;QACxB,MAAM,cAAc,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { type FleetNodeDefinition } from '@agent-relay/fleet';
|
|
2
2
|
import type { CoreDependencies, CoreProjectPaths, CoreRelay } from '../commands/core.js';
|
|
3
3
|
import { type NodeDefinitionDescriptor } from './node-provider-child.js';
|
|
4
|
+
import { type WorkspaceSelection } from './project-workspace-key.js';
|
|
4
5
|
type UpOptions = {
|
|
5
6
|
spawn?: boolean;
|
|
6
7
|
background?: boolean;
|
|
8
|
+
/** Internal marker set only on the detached child re-exec. */
|
|
9
|
+
backgroundChild?: boolean;
|
|
7
10
|
verbose?: boolean;
|
|
8
11
|
workspaceKey?: string;
|
|
9
12
|
stateDir?: string;
|
|
@@ -31,11 +34,15 @@ type DownOptions = {
|
|
|
31
34
|
timeout?: string;
|
|
32
35
|
stateDir?: string;
|
|
33
36
|
};
|
|
37
|
+
export declare const WORKSPACE_BINDING_SOURCE_ENV = "AGENT_RELAY_WORKSPACE_SOURCE";
|
|
38
|
+
export type WorkspaceBindingSource = WorkspaceSelection['source'] | 'created' | 'multi-workspace';
|
|
34
39
|
export interface BrokerConnection {
|
|
35
40
|
url: string;
|
|
36
41
|
port: number;
|
|
37
42
|
api_key: string;
|
|
38
43
|
pid: number;
|
|
44
|
+
/** Non-secret provenance recorded by the CLI after the broker handshake. */
|
|
45
|
+
workspace_source?: WorkspaceBindingSource;
|
|
39
46
|
}
|
|
40
47
|
type NodeDeliveryStatus = {
|
|
41
48
|
tokenPresent: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"broker-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/broker-lifecycle.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"broker-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/cli/lib/broker-lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import fs from 'node:fs';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { HarnessDriverClient } from '@agent-relay/harness-driver';
|
|
4
5
|
import { startServeNode } from '@agent-relay/fleet';
|
|
5
6
|
import { createLogger } from '@agent-relay/utils';
|
|
7
|
+
import { redactCredentialValues } from '@agent-relay/cloud/redact';
|
|
6
8
|
import { track } from '../telemetry/index.js';
|
|
7
9
|
import { buildBundledAgentRelayMcpCommand } from './agent-relay-mcp-command.js';
|
|
8
10
|
import { errorClassName } from './telemetry-helpers.js';
|
|
@@ -12,13 +14,14 @@ import { describeNodeDefinitionViaNode, descriptorCapacitySource, startNodeJsNod
|
|
|
12
14
|
import { describeError } from './describe-error.js';
|
|
13
15
|
import { maskSecret } from './redact.js';
|
|
14
16
|
import { startReflexCapture } from './reflex-capture.js';
|
|
15
|
-
import {
|
|
16
|
-
import { promoteWorkspaceKeyEnvAlias } from './workspace-env.js';
|
|
17
|
+
import { readProjectWorkspaceSession, resolveWorkspaceSelection, writeProjectWorkspaceKey, } from './project-workspace-key.js';
|
|
17
18
|
const MAX_API_PORT_ATTEMPTS = 25;
|
|
18
19
|
const MAX_PORT = 65535;
|
|
19
20
|
const DEFAULT_BROKER_BASE_PORT = 3888;
|
|
20
21
|
/** The broker writes this file with URL, port, API key, and PID. */
|
|
21
22
|
const CONNECTION_FILENAME = 'connection.json';
|
|
23
|
+
const BACKGROUND_START_ERROR_FILENAME = 'background-start-error.log';
|
|
24
|
+
export const WORKSPACE_BINDING_SOURCE_ENV = 'AGENT_RELAY_WORKSPACE_SOURCE';
|
|
22
25
|
const STATUS_POLL_INTERVAL_MS = 500;
|
|
23
26
|
const DETACHED_START_READY_TIMEOUT_MS = 10_000;
|
|
24
27
|
const NODE_DELIVERY_READY_TIMEOUT_MS = 10_000;
|
|
@@ -188,7 +191,7 @@ export function describeErrorWithCause(err) {
|
|
|
188
191
|
parts.push(detail);
|
|
189
192
|
if (codes.length > 0)
|
|
190
193
|
parts.push(`[${codes.join(', ')}]`);
|
|
191
|
-
return parts.join(' — ');
|
|
194
|
+
return redactCredentialValues(parts.join(' — '));
|
|
192
195
|
}
|
|
193
196
|
/**
|
|
194
197
|
* Pick the best `error_class` for telemetry. Prefer a network-style code from
|
|
@@ -534,6 +537,77 @@ function safeUnlink(filePath, deps) {
|
|
|
534
537
|
// Best-effort cleanup.
|
|
535
538
|
}
|
|
536
539
|
}
|
|
540
|
+
function workspaceBindingSource(value) {
|
|
541
|
+
return value === 'flag' ||
|
|
542
|
+
value === 'env' ||
|
|
543
|
+
value === 'project' ||
|
|
544
|
+
value === 'store' ||
|
|
545
|
+
value === 'created' ||
|
|
546
|
+
value === 'multi-workspace'
|
|
547
|
+
? value
|
|
548
|
+
: undefined;
|
|
549
|
+
}
|
|
550
|
+
function workspaceBindingSourceLabel(source) {
|
|
551
|
+
switch (source) {
|
|
552
|
+
case 'flag':
|
|
553
|
+
return 'command-line flag (--workspace-key / --wk)';
|
|
554
|
+
case 'env':
|
|
555
|
+
return 'environment (RELAY_WORKSPACE_KEY > AGENT_RELAY_WORKSPACE_KEY > RELAY_API_KEY)';
|
|
556
|
+
case 'project':
|
|
557
|
+
return 'repository pin (.agentworkforce/relay/workspace-key.json)';
|
|
558
|
+
case 'store':
|
|
559
|
+
return 'machine-global active workspace (~/.agentworkforce/relay/workspaces.json)';
|
|
560
|
+
case 'created':
|
|
561
|
+
return 'created (no configured workspace resolved)';
|
|
562
|
+
case 'multi-workspace':
|
|
563
|
+
return 'multi-workspace session ($RELAY_WORKSPACES_JSON)';
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
/** True when `RELAY_WORKSPACES_JSON` carries at least one membership. The broker's
|
|
567
|
+
* `startup_session_set_with_options` checks this env var before any single
|
|
568
|
+
* workspace key (flag, env, repository pin, or machine-global store), so the
|
|
569
|
+
* CLI's precedence ladder must defer to it for provenance too — otherwise
|
|
570
|
+
* `node up` / `node status` can report a source the broker never used. */
|
|
571
|
+
function usesMultiWorkspaceEnv(env) {
|
|
572
|
+
return Boolean(env.RELAY_WORKSPACES_JSON?.trim());
|
|
573
|
+
}
|
|
574
|
+
function writeBrokerBindingSource(dataDir, source, deps) {
|
|
575
|
+
const connectionPath = path.join(dataDir, CONNECTION_FILENAME);
|
|
576
|
+
const connection = readBrokerConnectionFromFs(deps.fs, dataDir);
|
|
577
|
+
if (!connection)
|
|
578
|
+
return;
|
|
579
|
+
// Every CLI invocation resolves the broker through this file, so a
|
|
580
|
+
// concurrent writer must never observe a partial or clobbered write.
|
|
581
|
+
// Write to a private tmp file and rename it into place, which is atomic
|
|
582
|
+
// on the same filesystem.
|
|
583
|
+
const tmpPath = `${connectionPath}.tmp-${process.pid}-${randomUUID()}`;
|
|
584
|
+
deps.fs.writeFileSync(tmpPath, `${JSON.stringify({ ...connection, workspace_source: source }, null, 2)}\n`, 'utf-8');
|
|
585
|
+
deps.fs.renameSync(tmpPath, connectionPath);
|
|
586
|
+
}
|
|
587
|
+
function backgroundStartErrorPath(dataDir) {
|
|
588
|
+
return path.join(dataDir, BACKGROUND_START_ERROR_FILENAME);
|
|
589
|
+
}
|
|
590
|
+
function readBackgroundStartError(dataDir, deps) {
|
|
591
|
+
try {
|
|
592
|
+
return deps.fs.readFileSync(backgroundStartErrorPath(dataDir), 'utf-8').trim() || undefined;
|
|
593
|
+
}
|
|
594
|
+
catch {
|
|
595
|
+
return undefined;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
function recordBackgroundStartError(message, dataDir, isDetachedChild, deps) {
|
|
599
|
+
if (!isDetachedChild)
|
|
600
|
+
return;
|
|
601
|
+
try {
|
|
602
|
+
// Never trust a project-loaded environment variable as a filesystem path.
|
|
603
|
+
// Detached startup owns one fixed diagnostic file inside its resolved
|
|
604
|
+
// broker state directory; foreground failures do not write it at all.
|
|
605
|
+
deps.fs.writeFileSync(backgroundStartErrorPath(dataDir), `${message}\n`, 'utf-8');
|
|
606
|
+
}
|
|
607
|
+
catch {
|
|
608
|
+
// Diagnostics must never replace the original startup error.
|
|
609
|
+
}
|
|
610
|
+
}
|
|
537
611
|
function readBrokerPid(dataDir, _deps) {
|
|
538
612
|
const conn = readBrokerConnectionFromFs(_deps.fs, dataDir);
|
|
539
613
|
return conn?.pid ?? null;
|
|
@@ -739,6 +813,7 @@ function cleanupBrokerFiles(paths, deps) {
|
|
|
739
813
|
safeUnlink(path.join(paths.dataDir, CONNECTION_FILENAME), deps);
|
|
740
814
|
safeUnlink(relaySockPath, deps);
|
|
741
815
|
safeUnlink(runtimePath, deps);
|
|
816
|
+
safeUnlink(backgroundStartErrorPath(paths.dataDir), deps);
|
|
742
817
|
// Clean up lock files and legacy pid files
|
|
743
818
|
try {
|
|
744
819
|
for (const file of deps.fs.readdirSync(paths.dataDir)) {
|
|
@@ -777,6 +852,9 @@ function childUpArgsForDetachedStart(options, deps) {
|
|
|
777
852
|
if (options.verbose === true && !args.includes('--verbose')) {
|
|
778
853
|
args.push('--verbose');
|
|
779
854
|
}
|
|
855
|
+
if (!args.includes('--background-child')) {
|
|
856
|
+
args.push('--background-child');
|
|
857
|
+
}
|
|
780
858
|
return args;
|
|
781
859
|
}
|
|
782
860
|
/** Drop each named option and, for the space-separated form, its value token. */
|
|
@@ -859,11 +937,14 @@ async function checkBrokerReadiness(paths, deps, requireApi) {
|
|
|
859
937
|
}
|
|
860
938
|
return { state: 'starting', conn };
|
|
861
939
|
}
|
|
862
|
-
async function waitForBrokerReadiness(paths, deps, waitMs, requireApi, verbose) {
|
|
940
|
+
async function waitForBrokerReadiness(paths, deps, waitMs, requireApi, verbose, stopWhenPidExits) {
|
|
863
941
|
const deadline = deps.now() + waitMs;
|
|
864
942
|
let latest = await checkBrokerReadiness(paths, deps, requireApi);
|
|
865
943
|
vlog(deps, verbose, `Broker readiness: ${latest.state}`);
|
|
866
944
|
while (latest.state !== 'running' && waitMs > 0 && deps.now() < deadline) {
|
|
945
|
+
if (stopWhenPidExits && !isProcessRunning(stopWhenPidExits, deps)) {
|
|
946
|
+
return latest;
|
|
947
|
+
}
|
|
867
948
|
await deps.sleep(Math.min(STATUS_POLL_INTERVAL_MS, Math.max(0, deadline - deps.now())));
|
|
868
949
|
const previousState = latest.state;
|
|
869
950
|
latest = await checkBrokerReadiness(paths, deps, requireApi);
|
|
@@ -988,39 +1069,58 @@ function planCapacitySource(plan) {
|
|
|
988
1069
|
}
|
|
989
1070
|
return plan.mode === 'in-process' ? plan.definition : descriptorCapacitySource(plan.descriptor);
|
|
990
1071
|
}
|
|
991
|
-
/**
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
return {
|
|
1001
|
-
workspaceKey,
|
|
1002
|
-
...(enrolledNodeId ? { enrolledNodeId } : {}),
|
|
1003
|
-
};
|
|
1004
|
-
}
|
|
1005
|
-
catch {
|
|
1072
|
+
/**
|
|
1073
|
+
* Apply the resolved workspace to the environment the broker (and any detached
|
|
1074
|
+
* child) inherits, and report which source won. Returns the pinned project
|
|
1075
|
+
* session when the repository pin supplied the selection.
|
|
1076
|
+
*/
|
|
1077
|
+
function applyWorkspaceSelection(selection, deps, projectDataDir) {
|
|
1078
|
+
if (!selection) {
|
|
1079
|
+
deps.log('Workspace: none selected (no --workspace-key, no RELAY_WORKSPACE_KEY, no repository pin, ' +
|
|
1080
|
+
'no active workspace in the machine-global store). A new workspace will be created.');
|
|
1006
1081
|
return undefined;
|
|
1007
1082
|
}
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1083
|
+
deps.log(`Workspace source: ${describeWorkspaceSource(selection.source)} (${selection.origin})`);
|
|
1084
|
+
// Normalize every winning source to the primary env var inherited by the
|
|
1085
|
+
// broker and any detached child. Keep a caller-supplied RELAY_API_KEY intact
|
|
1086
|
+
// when an explicit flag or environment variable won.
|
|
1087
|
+
deps.env.RELAY_WORKSPACE_KEY = selection.key;
|
|
1088
|
+
if (selection.source === 'project' || selection.source === 'store') {
|
|
1089
|
+
deps.env.RELAY_API_KEY = selection.key;
|
|
1090
|
+
}
|
|
1091
|
+
if (selection.source !== 'project') {
|
|
1013
1092
|
return undefined;
|
|
1014
1093
|
}
|
|
1015
|
-
const
|
|
1016
|
-
if (
|
|
1017
|
-
deps.env.
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1094
|
+
const pinned = readProjectWorkspaceSession(projectDataDir, deps.fs);
|
|
1095
|
+
if (pinned?.enrolledNodeId) {
|
|
1096
|
+
deps.env.AGENT_RELAY_ENROLLED_NODE_ID = pinned.enrolledNodeId;
|
|
1097
|
+
}
|
|
1098
|
+
return pinned;
|
|
1099
|
+
}
|
|
1100
|
+
/** Human-readable name for a precedence-ladder step, for the startup line. */
|
|
1101
|
+
function describeWorkspaceSource(source) {
|
|
1102
|
+
switch (source) {
|
|
1103
|
+
case 'flag':
|
|
1104
|
+
return 'command-line flag';
|
|
1105
|
+
case 'env':
|
|
1106
|
+
return 'environment';
|
|
1107
|
+
case 'project':
|
|
1108
|
+
return 'repository pin';
|
|
1109
|
+
case 'store':
|
|
1110
|
+
return 'machine-global active workspace';
|
|
1022
1111
|
}
|
|
1023
|
-
|
|
1112
|
+
}
|
|
1113
|
+
/**
|
|
1114
|
+
* Preserve the original source across `--background` re-exec. The detached
|
|
1115
|
+
* child sees the normalized RELAY_WORKSPACE_KEY as an env selection, so this
|
|
1116
|
+
* marker carries only provenance; it never participates in resolution.
|
|
1117
|
+
*/
|
|
1118
|
+
function recordWorkspaceBindingSource(selection, deps, overrideSource) {
|
|
1119
|
+
const inheritedSource = workspaceBindingSource(deps.env[WORKSPACE_BINDING_SOURCE_ENV]);
|
|
1120
|
+
const source = overrideSource ??
|
|
1121
|
+
(selection?.source === 'env' && inheritedSource ? inheritedSource : (selection?.source ?? 'created'));
|
|
1122
|
+
deps.env[WORKSPACE_BINDING_SOURCE_ENV] = source;
|
|
1123
|
+
return source;
|
|
1024
1124
|
}
|
|
1025
1125
|
export async function runUpCommand(options, deps) {
|
|
1026
1126
|
ensureBundledAgentRelayMcpCommand(deps);
|
|
@@ -1031,7 +1131,25 @@ export async function runUpCommand(options, deps) {
|
|
|
1031
1131
|
// --state-dir), so the key must be persisted here even when broker state is
|
|
1032
1132
|
// redirected elsewhere.
|
|
1033
1133
|
const projectWorkspaceKeyDataDir = paths.dataDir;
|
|
1034
|
-
|
|
1134
|
+
// The broker's startup_session_set_with_options() checks RELAY_WORKSPACES_JSON
|
|
1135
|
+
// before any single workspace key, so a flag/env/pin/store resolution here
|
|
1136
|
+
// would report provenance the broker never actually used.
|
|
1137
|
+
const joinsMultiWorkspaceSession = usesMultiWorkspaceEnv(deps.env);
|
|
1138
|
+
const workspaceSelection = joinsMultiWorkspaceSession
|
|
1139
|
+
? undefined
|
|
1140
|
+
: resolveWorkspaceSelection({
|
|
1141
|
+
workspaceKey: options.workspaceKey,
|
|
1142
|
+
env: deps.env,
|
|
1143
|
+
projectDataDir: projectWorkspaceKeyDataDir,
|
|
1144
|
+
fileSystem: deps.fs,
|
|
1145
|
+
});
|
|
1146
|
+
const workspaceBindingSource = recordWorkspaceBindingSource(workspaceSelection, deps, joinsMultiWorkspaceSession ? 'multi-workspace' : undefined);
|
|
1147
|
+
const resumedProjectSession = joinsMultiWorkspaceSession
|
|
1148
|
+
? undefined
|
|
1149
|
+
: applyWorkspaceSelection(workspaceSelection, deps, projectWorkspaceKeyDataDir);
|
|
1150
|
+
if (joinsMultiWorkspaceSession) {
|
|
1151
|
+
deps.log(`Workspace source: ${workspaceBindingSourceLabel('multi-workspace')}`);
|
|
1152
|
+
}
|
|
1035
1153
|
// --state-dir overrides where the broker writes state / connection files
|
|
1036
1154
|
if (options.stateDir) {
|
|
1037
1155
|
const resolved = path.resolve(options.stateDir);
|
|
@@ -1062,6 +1180,8 @@ export async function runUpCommand(options, deps) {
|
|
|
1062
1180
|
deps.exit(1);
|
|
1063
1181
|
return;
|
|
1064
1182
|
}
|
|
1183
|
+
const startErrorPath = backgroundStartErrorPath(paths.dataDir);
|
|
1184
|
+
safeUnlink(startErrorPath, deps);
|
|
1065
1185
|
const args = childUpArgsForDetachedStart(options, deps);
|
|
1066
1186
|
const invocation = detachedCliInvocation(deps, args);
|
|
1067
1187
|
let child;
|
|
@@ -1079,18 +1199,31 @@ export async function runUpCommand(options, deps) {
|
|
|
1079
1199
|
}
|
|
1080
1200
|
child.unref?.();
|
|
1081
1201
|
vlog(deps, options.verbose, `Spawned detached broker child (pid: ${child.pid ?? 'unknown'}), waiting for readiness...`);
|
|
1082
|
-
const readiness = await waitForBrokerReadiness(paths, deps, DETACHED_START_READY_TIMEOUT_MS, true, options.verbose);
|
|
1202
|
+
const readiness = await waitForBrokerReadiness(paths, deps, DETACHED_START_READY_TIMEOUT_MS, true, options.verbose, child.pid);
|
|
1083
1203
|
if (readiness.state !== 'running') {
|
|
1084
1204
|
const pid = readiness.state === 'starting' ? readiness.conn.pid : child.pid;
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1205
|
+
const childExited = typeof child.pid === 'number' && child.pid > 0 && !isProcessRunning(child.pid, deps);
|
|
1206
|
+
if (childExited) {
|
|
1207
|
+
deps.error(`Broker background child exited before becoming ready (pid: ${child.pid}).`);
|
|
1208
|
+
}
|
|
1209
|
+
else {
|
|
1210
|
+
deps.error(pid
|
|
1211
|
+
? `Broker background start did not become ready within ${DETACHED_START_READY_TIMEOUT_MS / 1000}s (pid: ${pid}).`
|
|
1212
|
+
: `Broker background start did not become ready within ${DETACHED_START_READY_TIMEOUT_MS / 1000}s.`);
|
|
1213
|
+
}
|
|
1088
1214
|
if (readiness.state === 'starting') {
|
|
1089
1215
|
deps.error('Broker process is running, but the API did not become ready.');
|
|
1090
1216
|
}
|
|
1217
|
+
const detachedError = readBackgroundStartError(paths.dataDir, deps);
|
|
1218
|
+
if (detachedError) {
|
|
1219
|
+
deps.error(`Detached broker error: ${detachedError}`);
|
|
1220
|
+
}
|
|
1221
|
+
else if (childExited) {
|
|
1222
|
+
deps.error('Retry without --background to see the broker startup error.');
|
|
1223
|
+
}
|
|
1091
1224
|
deps.error('Run `agent-relay status --wait-for=10` for details, or `agent-relay down --force` to clean up.');
|
|
1092
1225
|
const cleanupPids = new Set();
|
|
1093
|
-
if (typeof child.pid === 'number' && child.pid > 0) {
|
|
1226
|
+
if (typeof child.pid === 'number' && child.pid > 0 && isProcessRunning(child.pid, deps)) {
|
|
1094
1227
|
cleanupPids.add(child.pid);
|
|
1095
1228
|
}
|
|
1096
1229
|
if (readiness.state === 'starting') {
|
|
@@ -1150,6 +1283,7 @@ export async function runUpCommand(options, deps) {
|
|
|
1150
1283
|
deps.log('Broker started.');
|
|
1151
1284
|
deps.log(`Broker PID: ${readiness.conn.pid}`);
|
|
1152
1285
|
deps.log('Stop with: agent-relay down');
|
|
1286
|
+
safeUnlink(startErrorPath, deps);
|
|
1153
1287
|
deps.exit(0);
|
|
1154
1288
|
return;
|
|
1155
1289
|
}
|
|
@@ -1207,10 +1341,30 @@ export async function runUpCommand(options, deps) {
|
|
|
1207
1341
|
await killOrphanedBrokerProcesses(paths.projectRoot, deps);
|
|
1208
1342
|
const started = await startBrokerWithPortFallback(paths, basePort, deps, options.brokerName, options.verbose);
|
|
1209
1343
|
relay = started.relay;
|
|
1344
|
+
try {
|
|
1345
|
+
writeBrokerBindingSource(paths.dataDir, workspaceBindingSource, deps);
|
|
1346
|
+
}
|
|
1347
|
+
catch {
|
|
1348
|
+
// Provenance is diagnostic metadata; a broker that came up stays up.
|
|
1349
|
+
}
|
|
1350
|
+
safeUnlink(backgroundStartErrorPath(paths.dataDir), deps);
|
|
1210
1351
|
deps.log(`Relay API: http://localhost:${started.apiPort}`);
|
|
1211
1352
|
deps.log(`Project: ${paths.projectRoot}`);
|
|
1212
1353
|
deps.log('Mode: broker (stdio)');
|
|
1213
1354
|
deps.log(`Workspace Key: ${relay.workspaceKey ? maskSecret(relay.workspaceKey) : 'unknown'}`);
|
|
1355
|
+
// Minting must be observable: without this line "created a workspace" and
|
|
1356
|
+
// "joined the pinned workspace" print identically.
|
|
1357
|
+
const joinedWorkspaceId = relay.workspaceId ?? 'unknown';
|
|
1358
|
+
// The multi-workspace session always joins a configured membership; it
|
|
1359
|
+
// never mints a new workspace the way an unresolved single key does.
|
|
1360
|
+
if (workspaceSelection || joinsMultiWorkspaceSession) {
|
|
1361
|
+
deps.log(`Workspace: joined ${joinedWorkspaceId}`);
|
|
1362
|
+
}
|
|
1363
|
+
else {
|
|
1364
|
+
deps.log(`Workspace: created new workspace ${joinedWorkspaceId}`);
|
|
1365
|
+
deps.log('Pin a workspace for this repository with `agent-relay up --workspace-key <key>`, ' +
|
|
1366
|
+
'or select one machine-wide with `agent-relay workspace switch <name>`.');
|
|
1367
|
+
}
|
|
1214
1368
|
deps.log('Broker started.');
|
|
1215
1369
|
// Record the workspace this broker joined (explicitly passed or auto-minted)
|
|
1216
1370
|
// in the DEFAULT project data dir (not any --state-dir override), so later
|
|
@@ -1220,6 +1374,10 @@ export async function runUpCommand(options, deps) {
|
|
|
1220
1374
|
try {
|
|
1221
1375
|
writeProjectWorkspaceKey(projectWorkspaceKeyDataDir, relay.workspaceKey ?? undefined, {
|
|
1222
1376
|
enrolledNodeId: deps.env.AGENT_RELAY_ENROLLED_NODE_ID ?? resumedProjectSession?.enrolledNodeId,
|
|
1377
|
+
// Recording the resolved workspace id lets the NEXT start detect a
|
|
1378
|
+
// conflicting source (a stored enrollment in another workspace) before
|
|
1379
|
+
// the broker comes up, instead of after agents land in the wrong place.
|
|
1380
|
+
workspaceId: relay.workspaceId ?? resumedProjectSession?.workspaceId,
|
|
1223
1381
|
});
|
|
1224
1382
|
}
|
|
1225
1383
|
catch {
|
|
@@ -1297,11 +1455,13 @@ export async function runUpCommand(options, deps) {
|
|
|
1297
1455
|
stage,
|
|
1298
1456
|
error_class: classifyBrokerStartError(err),
|
|
1299
1457
|
});
|
|
1458
|
+
const detailedMessage = describeErrorWithCause(err);
|
|
1459
|
+
recordBackgroundStartError(detailedMessage, paths.dataDir, options.backgroundChild === true, deps);
|
|
1300
1460
|
if (isBrokerAlreadyRunningError(message)) {
|
|
1301
1461
|
reportAlreadyRunningError(message, paths.dataDir, deps);
|
|
1302
1462
|
}
|
|
1303
1463
|
else {
|
|
1304
|
-
deps.error(`Failed to start broker: ${
|
|
1464
|
+
deps.error(`Failed to start broker: ${detailedMessage}`);
|
|
1305
1465
|
}
|
|
1306
1466
|
deps.exit(1);
|
|
1307
1467
|
}
|
|
@@ -1447,6 +1607,10 @@ export async function runStatusCommand(deps, options) {
|
|
|
1447
1607
|
deps.log('Mode: broker (stdio)');
|
|
1448
1608
|
deps.log(`PID: ${readiness.conn.pid}`);
|
|
1449
1609
|
deps.log(`Project: ${paths.projectRoot}`);
|
|
1610
|
+
const source = workspaceBindingSource(readiness.conn.workspace_source);
|
|
1611
|
+
deps.log(source
|
|
1612
|
+
? `Workspace source: ${workspaceBindingSourceLabel(source)}`
|
|
1613
|
+
: 'Workspace source: unknown (startup provenance was not recorded)');
|
|
1450
1614
|
// Query the running broker for additional status info
|
|
1451
1615
|
const statusDetails = readiness.statusDetails ?? (waitMs > 0 ? null : await readBrokerStatusDetails(readiness.conn));
|
|
1452
1616
|
if (statusDetails) {
|