agent-relay 5.0.0 → 6.0.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/dist/index.cjs +599 -197
- package/dist/packages/sdk/src/provisioner/local-jwks.d.ts +25 -0
- package/dist/packages/sdk/src/provisioner/local-jwks.d.ts.map +1 -0
- package/dist/packages/sdk/src/provisioner/local-jwks.js +70 -0
- package/dist/packages/sdk/src/provisioner/local-jwks.js.map +1 -0
- package/dist/packages/sdk/src/provisioner/token.d.ts +6 -3
- package/dist/packages/sdk/src/provisioner/token.d.ts.map +1 -1
- package/dist/packages/sdk/src/provisioner/token.js +11 -8
- package/dist/packages/sdk/src/provisioner/token.js.map +1 -1
- package/dist/src/cli/commands/on/provision.d.ts +2 -1
- package/dist/src/cli/commands/on/provision.d.ts.map +1 -1
- package/dist/src/cli/commands/on/provision.js +5 -4
- package/dist/src/cli/commands/on/provision.js.map +1 -1
- package/dist/src/cli/commands/on/services.d.ts +1 -0
- package/dist/src/cli/commands/on/services.d.ts.map +1 -1
- package/dist/src/cli/commands/on/services.js +9 -0
- package/dist/src/cli/commands/on/services.js.map +1 -1
- package/dist/src/cli/commands/on/start.d.ts +6 -3
- package/dist/src/cli/commands/on/start.d.ts.map +1 -1
- package/dist/src/cli/commands/on/start.js +310 -297
- package/dist/src/cli/commands/on/start.js.map +1 -1
- package/dist/src/cli/commands/on/token.d.ts +3 -1
- package/dist/src/cli/commands/on/token.d.ts.map +1 -1
- package/dist/src/cli/commands/on/token.js +3 -3
- package/dist/src/cli/commands/on/token.js.map +1 -1
- package/node_modules/@agent-relay/broker-darwin-arm64/README.md +11 -0
- package/node_modules/@agent-relay/broker-darwin-arm64/package.json +17 -0
- package/node_modules/@agent-relay/broker-darwin-x64/README.md +11 -0
- package/node_modules/@agent-relay/broker-darwin-x64/bin/.gitkeep +0 -0
- package/node_modules/@agent-relay/broker-darwin-x64/package.json +17 -0
- package/node_modules/@agent-relay/broker-linux-arm64/README.md +12 -0
- package/node_modules/@agent-relay/broker-linux-arm64/bin/.gitkeep +0 -0
- package/node_modules/@agent-relay/broker-linux-arm64/package.json +17 -0
- package/node_modules/@agent-relay/broker-linux-x64/README.md +12 -0
- package/node_modules/@agent-relay/broker-linux-x64/bin/.gitkeep +0 -0
- package/node_modules/@agent-relay/broker-linux-x64/package.json +17 -0
- package/node_modules/@agent-relay/broker-win32-x64/README.md +11 -0
- package/node_modules/@agent-relay/broker-win32-x64/bin/.gitkeep +0 -0
- package/node_modules/@agent-relay/broker-win32-x64/package.json +17 -0
- package/node_modules/@agent-relay/cloud/package.json +2 -2
- package/node_modules/@agent-relay/config/dist/cli-registry.generated.d.ts +353 -157
- package/node_modules/@agent-relay/config/dist/cli-registry.generated.d.ts.map +1 -1
- package/node_modules/@agent-relay/config/dist/cli-registry.generated.js +356 -160
- package/node_modules/@agent-relay/config/dist/cli-registry.generated.js.map +1 -1
- package/node_modules/@agent-relay/config/package.json +1 -1
- package/node_modules/@agent-relay/hooks/package.json +4 -4
- package/node_modules/@agent-relay/sdk/dist/broker-path.d.ts +18 -7
- package/node_modules/@agent-relay/sdk/dist/broker-path.d.ts.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/broker-path.js +92 -20
- package/node_modules/@agent-relay/sdk/dist/broker-path.js.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/client.d.ts.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/client.js +9 -2
- package/node_modules/@agent-relay/sdk/dist/client.js.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/provisioner/__tests__/audit.test.js +2 -2
- package/node_modules/@agent-relay/sdk/dist/provisioner/__tests__/audit.test.js.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/provisioner/__tests__/token-factory.test.js +29 -17
- package/node_modules/@agent-relay/sdk/dist/provisioner/__tests__/token-factory.test.js.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/provisioner/__tests__/token.test.js +8 -3
- package/node_modules/@agent-relay/sdk/dist/provisioner/__tests__/token.test.js.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/provisioner/index.d.ts +1 -0
- package/node_modules/@agent-relay/sdk/dist/provisioner/index.d.ts.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/provisioner/index.js +5 -2
- package/node_modules/@agent-relay/sdk/dist/provisioner/index.js.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/provisioner/local-jwks.d.ts +25 -0
- package/node_modules/@agent-relay/sdk/dist/provisioner/local-jwks.d.ts.map +1 -0
- package/node_modules/@agent-relay/sdk/dist/provisioner/local-jwks.js +70 -0
- package/node_modules/@agent-relay/sdk/dist/provisioner/local-jwks.js.map +1 -0
- package/node_modules/@agent-relay/sdk/dist/provisioner/token.d.ts +6 -3
- package/node_modules/@agent-relay/sdk/dist/provisioner/token.d.ts.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/provisioner/token.js +11 -8
- package/node_modules/@agent-relay/sdk/dist/provisioner/token.js.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/provisioner/types.d.ts +3 -2
- package/node_modules/@agent-relay/sdk/dist/provisioner/types.d.ts.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/workflows/__tests__/sibling-links.test.d.ts +2 -0
- package/node_modules/@agent-relay/sdk/dist/workflows/__tests__/sibling-links.test.d.ts.map +1 -0
- package/node_modules/@agent-relay/sdk/dist/workflows/__tests__/sibling-links.test.js +166 -0
- package/node_modules/@agent-relay/sdk/dist/workflows/__tests__/sibling-links.test.js.map +1 -0
- package/node_modules/@agent-relay/sdk/dist/workflows/index.d.ts +2 -0
- package/node_modules/@agent-relay/sdk/dist/workflows/index.d.ts.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/workflows/index.js +1 -0
- package/node_modules/@agent-relay/sdk/dist/workflows/index.js.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/workflows/runner.d.ts.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/workflows/runner.js +18 -1
- package/node_modules/@agent-relay/sdk/dist/workflows/runner.js.map +1 -1
- package/node_modules/@agent-relay/sdk/dist/workflows/sibling-links.d.ts +100 -0
- package/node_modules/@agent-relay/sdk/dist/workflows/sibling-links.d.ts.map +1 -0
- package/node_modules/@agent-relay/sdk/dist/workflows/sibling-links.js +205 -0
- package/node_modules/@agent-relay/sdk/dist/workflows/sibling-links.js.map +1 -0
- package/node_modules/@agent-relay/sdk/package.json +10 -3
- package/node_modules/@agent-relay/telemetry/package.json +1 -1
- package/node_modules/@agent-relay/trajectory/package.json +2 -2
- package/node_modules/@agent-relay/user-directory/package.json +2 -2
- package/node_modules/@agent-relay/utils/package.json +2 -2
- package/node_modules/@aws-sdk/core/package.json +5 -5
- package/node_modules/@aws-sdk/credential-provider-env/package.json +2 -2
- package/node_modules/@aws-sdk/credential-provider-http/package.json +5 -5
- package/node_modules/@aws-sdk/credential-provider-ini/package.json +9 -9
- package/node_modules/@aws-sdk/credential-provider-login/package.json +3 -3
- package/node_modules/@aws-sdk/credential-provider-node/package.json +7 -7
- package/node_modules/@aws-sdk/credential-provider-process/package.json +2 -2
- package/node_modules/@aws-sdk/credential-provider-sso/package.json +4 -4
- package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +3 -3
- package/node_modules/@aws-sdk/middleware-flexible-checksums/package.json +4 -4
- package/node_modules/@aws-sdk/middleware-sdk-s3/package.json +5 -5
- package/node_modules/@aws-sdk/middleware-user-agent/package.json +4 -4
- package/node_modules/@aws-sdk/nested-clients/package.json +14 -14
- package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +2 -2
- package/node_modules/@aws-sdk/token-providers/package.json +3 -3
- package/node_modules/@aws-sdk/util-user-agent-node/package.json +2 -2
- package/node_modules/@aws-sdk/xml-builder/dist-cjs/xml-parser.js +0 -2
- package/node_modules/@aws-sdk/xml-builder/dist-es/xml-parser.js +0 -2
- package/node_modules/@aws-sdk/xml-builder/package.json +2 -2
- package/node_modules/@nodable/entities/README.md +41 -0
- package/node_modules/@nodable/entities/package.json +54 -0
- package/node_modules/@nodable/entities/src/EntityDecoder.js +543 -0
- package/node_modules/@nodable/entities/src/EntityEncoder.js +194 -0
- package/node_modules/@nodable/entities/src/entities.js +1177 -0
- package/node_modules/@nodable/entities/src/entityTries.js +49 -0
- package/node_modules/@nodable/entities/src/index.d.ts +264 -0
- package/node_modules/@nodable/entities/src/index.js +29 -0
- package/node_modules/@smithy/core/package.json +2 -2
- package/node_modules/@smithy/middleware-endpoint/package.json +3 -3
- package/node_modules/@smithy/middleware-retry/package.json +4 -4
- package/node_modules/@smithy/middleware-serde/package.json +2 -2
- package/node_modules/@smithy/node-http-handler/dist-cjs/index.js +27 -16
- package/node_modules/@smithy/node-http-handler/dist-es/http2/ClientHttp2SessionRef.js +5 -0
- package/node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-manager.js +22 -16
- package/node_modules/@smithy/node-http-handler/dist-types/http2/ClientHttp2SessionRef.d.ts +4 -0
- package/node_modules/@smithy/node-http-handler/dist-types/node-http2-connection-manager.d.ts +2 -4
- package/node_modules/@smithy/node-http-handler/package.json +1 -1
- package/node_modules/@smithy/smithy-client/package.json +4 -4
- package/node_modules/@smithy/util-defaults-mode-browser/package.json +2 -2
- package/node_modules/@smithy/util-defaults-mode-node/package.json +2 -2
- package/node_modules/@smithy/util-retry/dist-cjs/index.js +20 -10
- package/node_modules/@smithy/util-retry/dist-es/StandardRetryStrategy.js +20 -10
- package/node_modules/@smithy/util-retry/dist-types/StandardRetryStrategy.d.ts +12 -4
- package/node_modules/@smithy/util-retry/package.json +1 -1
- package/node_modules/@smithy/util-stream/package.json +2 -2
- package/node_modules/fast-xml-parser/CHANGELOG.md +53 -0
- package/node_modules/fast-xml-parser/README.md +8 -28
- package/node_modules/fast-xml-parser/lib/fxbuilder.min.js +1 -1
- package/node_modules/fast-xml-parser/lib/fxbuilder.min.js.map +1 -1
- package/node_modules/fast-xml-parser/lib/fxp.cjs +1 -1
- package/node_modules/fast-xml-parser/lib/fxp.d.cts +172 -6
- package/node_modules/fast-xml-parser/lib/fxp.min.js +1 -1
- package/node_modules/fast-xml-parser/lib/fxp.min.js.map +1 -1
- package/node_modules/fast-xml-parser/lib/fxparser.min.js +1 -1
- package/node_modules/fast-xml-parser/lib/fxparser.min.js.map +1 -1
- package/node_modules/fast-xml-parser/package.json +5 -4
- package/node_modules/fast-xml-parser/src/fxp.d.ts +162 -3
- package/node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js +2 -5
- package/node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js +15 -11
- package/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js +168 -244
- package/node_modules/fast-xml-parser/src/xmlparser/XMLParser.js +1 -1
- package/package.json +9 -10
- package/packages/cloud/package.json +2 -2
- package/packages/config/dist/cli-registry.generated.d.ts +353 -157
- package/packages/config/dist/cli-registry.generated.d.ts.map +1 -1
- package/packages/config/dist/cli-registry.generated.js +356 -160
- package/packages/config/dist/cli-registry.generated.js.map +1 -1
- package/packages/config/package.json +1 -1
- package/packages/hooks/package.json +4 -4
- package/packages/sdk/dist/broker-path.d.ts +18 -7
- package/packages/sdk/dist/broker-path.d.ts.map +1 -1
- package/packages/sdk/dist/broker-path.js +92 -20
- package/packages/sdk/dist/broker-path.js.map +1 -1
- package/packages/sdk/dist/client.d.ts.map +1 -1
- package/packages/sdk/dist/client.js +9 -2
- package/packages/sdk/dist/client.js.map +1 -1
- package/packages/sdk/dist/provisioner/__tests__/audit.test.js +2 -2
- package/packages/sdk/dist/provisioner/__tests__/audit.test.js.map +1 -1
- package/packages/sdk/dist/provisioner/__tests__/token-factory.test.js +29 -17
- package/packages/sdk/dist/provisioner/__tests__/token-factory.test.js.map +1 -1
- package/packages/sdk/dist/provisioner/__tests__/token.test.js +8 -3
- package/packages/sdk/dist/provisioner/__tests__/token.test.js.map +1 -1
- package/packages/sdk/dist/provisioner/index.d.ts +1 -0
- package/packages/sdk/dist/provisioner/index.d.ts.map +1 -1
- package/packages/sdk/dist/provisioner/index.js +5 -2
- package/packages/sdk/dist/provisioner/index.js.map +1 -1
- package/packages/sdk/dist/provisioner/local-jwks.d.ts +25 -0
- package/packages/sdk/dist/provisioner/local-jwks.d.ts.map +1 -0
- package/packages/sdk/dist/provisioner/local-jwks.js +70 -0
- package/packages/sdk/dist/provisioner/local-jwks.js.map +1 -0
- package/packages/sdk/dist/provisioner/token.d.ts +6 -3
- package/packages/sdk/dist/provisioner/token.d.ts.map +1 -1
- package/packages/sdk/dist/provisioner/token.js +11 -8
- package/packages/sdk/dist/provisioner/token.js.map +1 -1
- package/packages/sdk/dist/provisioner/types.d.ts +3 -2
- package/packages/sdk/dist/provisioner/types.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/__tests__/sibling-links.test.d.ts +2 -0
- package/packages/sdk/dist/workflows/__tests__/sibling-links.test.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/sibling-links.test.js +166 -0
- package/packages/sdk/dist/workflows/__tests__/sibling-links.test.js.map +1 -0
- package/packages/sdk/dist/workflows/index.d.ts +2 -0
- package/packages/sdk/dist/workflows/index.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/index.js +1 -0
- package/packages/sdk/dist/workflows/index.js.map +1 -1
- package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/runner.js +18 -1
- package/packages/sdk/dist/workflows/runner.js.map +1 -1
- package/packages/sdk/dist/workflows/sibling-links.d.ts +100 -0
- package/packages/sdk/dist/workflows/sibling-links.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/sibling-links.js +205 -0
- package/packages/sdk/dist/workflows/sibling-links.js.map +1 -0
- package/packages/sdk/package.json +10 -3
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/package.json +2 -2
- package/scripts/postinstall.js +9 -146
- package/bin/agent-relay-broker-darwin-arm64 +0 -0
- package/bin/agent-relay-broker-darwin-x64 +0 -0
- package/bin/agent-relay-broker-linux-arm64 +0 -0
- package/bin/agent-relay-broker-linux-x64 +0 -0
- package/node_modules/fast-xml-parser/lib/pem.d.cts +0 -148
- package/node_modules/fast-xml-parser/src/pem.d.ts +0 -135
- /package/{bin → node_modules/@agent-relay/broker-darwin-arm64/bin}/.gitkeep +0 -0
|
@@ -10,6 +10,7 @@ import { launchOnMount } from '@relayfile/local-mount';
|
|
|
10
10
|
import { mintToken } from './token.js';
|
|
11
11
|
import { seedAclRules } from './workspace.js';
|
|
12
12
|
import { seedWorkspace } from '../../../../packages/sdk/src/provisioner/seeder.js';
|
|
13
|
+
import { createLocalJwks, exportPrivateKeyPem, RELAYAUTH_JWKS_URL_ENV, RELAYAUTH_JWT_KID_ENV, RELAYAUTH_JWT_PRIVATE_KEY_PEM_ENV, } from '../../../../packages/sdk/src/provisioner/local-jwks.js';
|
|
13
14
|
import { ensureAuthenticated, readStoredAuth } from '@agent-relay/cloud';
|
|
14
15
|
const DEFAULT_SEED_EXCLUDES = ['.relay', '.git', 'node_modules'];
|
|
15
16
|
const DEFAULT_RELAYCAST_BASE_URL = 'https://api.relaycast.dev';
|
|
@@ -279,13 +280,13 @@ async function createRelaycastWorkspace(fetchFn, baseUrl, workspaceName) {
|
|
|
279
280
|
async function requestLocalWorkspaceSession(options) {
|
|
280
281
|
const fetchFn = options.fetchFn ?? fetch;
|
|
281
282
|
const relayDir = options.relayDir;
|
|
282
|
-
const
|
|
283
|
+
const tokenSigningKey = options.tokenSigningKey;
|
|
283
284
|
const requestedWorkspaceId = normalizeWorkspaceId(options.requestedWorkspaceId);
|
|
284
285
|
if (!relayDir) {
|
|
285
286
|
throw new Error('relayDir is required for local workspace sessions');
|
|
286
287
|
}
|
|
287
|
-
if (!
|
|
288
|
-
throw new Error('
|
|
288
|
+
if (!tokenSigningKey) {
|
|
289
|
+
throw new Error('tokenSigningKey is required for local workspace sessions');
|
|
289
290
|
}
|
|
290
291
|
const workspaceId = requestedWorkspaceId ?? generateWorkspaceId();
|
|
291
292
|
const existing = readWorkspaceRegistry(relayDir)[workspaceId];
|
|
@@ -315,7 +316,8 @@ async function requestLocalWorkspaceSession(options) {
|
|
|
315
316
|
created: !requestedWorkspaceId,
|
|
316
317
|
workspaceId,
|
|
317
318
|
token: mintToken({
|
|
318
|
-
|
|
319
|
+
privateKey: tokenSigningKey.privateKey,
|
|
320
|
+
kid: tokenSigningKey.kid,
|
|
319
321
|
agentName: options.agentName,
|
|
320
322
|
workspace: workspaceId,
|
|
321
323
|
scopes: options.scopes,
|
|
@@ -330,7 +332,7 @@ export async function requestWorkspaceSession(options) {
|
|
|
330
332
|
const requestedWorkspaceId = normalizeWorkspaceId(options.requestedWorkspaceId);
|
|
331
333
|
if ((isLocalBaseUrl(options.authBase) || options.preferLocalSession) &&
|
|
332
334
|
options.relayDir &&
|
|
333
|
-
options.
|
|
335
|
+
options.tokenSigningKey) {
|
|
334
336
|
return requestLocalWorkspaceSession({ ...options, fetchFn, requestedWorkspaceId });
|
|
335
337
|
}
|
|
336
338
|
if (requestedWorkspaceId) {
|
|
@@ -418,10 +420,6 @@ function loadConfigFromFile(configPath, projectDir) {
|
|
|
418
420
|
const fallbackWorkspace = path.basename(projectDir);
|
|
419
421
|
const workspace = toString(payload.workspace, toString(root.workspace, fallbackWorkspace));
|
|
420
422
|
const signing_secret = toString(payload.signing_secret, toString(root.signing_secret, process.env.SIGNING_KEY ?? ''));
|
|
421
|
-
if (!signing_secret) {
|
|
422
|
-
throw new Error(`relay config at ${configPath} is missing signing_secret and SIGNING_KEY env var is not set. ` +
|
|
423
|
-
'Set signing_secret in your config or export SIGNING_KEY.');
|
|
424
|
-
}
|
|
425
423
|
const agents = normalizeAgents(payload.agents ?? root.agents);
|
|
426
424
|
return { workspace, signing_secret, agents };
|
|
427
425
|
}
|
|
@@ -730,10 +728,11 @@ function pickDeniedCount(syncOutput) {
|
|
|
730
728
|
const match = syncOutput.match(/skipping denied file/gi);
|
|
731
729
|
return match ? match.length : 0;
|
|
732
730
|
}
|
|
733
|
-
function generateTokenFromScript(config, agent, log, error) {
|
|
731
|
+
function generateTokenFromScript(config, agent, tokenSigningKey, log, error) {
|
|
734
732
|
try {
|
|
735
733
|
return mintToken({
|
|
736
|
-
|
|
734
|
+
privateKey: tokenSigningKey.privateKey,
|
|
735
|
+
kid: tokenSigningKey.kid,
|
|
737
736
|
agentName: agent.name,
|
|
738
737
|
workspace: config.workspace,
|
|
739
738
|
scopes: agent.scopes,
|
|
@@ -789,7 +788,7 @@ function getSandboxFlags(cli) {
|
|
|
789
788
|
return [];
|
|
790
789
|
}
|
|
791
790
|
}
|
|
792
|
-
async function ensureProvisioned(config, agent, relayfileRoot, projectDir, tokenPath, log, error, deps) {
|
|
791
|
+
async function ensureProvisioned(config, agent, tokenSigningKey, relayfileRoot, projectDir, tokenPath, log, error, deps) {
|
|
793
792
|
try {
|
|
794
793
|
return readFileSync(tokenPath, 'utf8').trim();
|
|
795
794
|
}
|
|
@@ -797,7 +796,7 @@ async function ensureProvisioned(config, agent, relayfileRoot, projectDir, token
|
|
|
797
796
|
// Token file does not exist yet — continue to provision
|
|
798
797
|
}
|
|
799
798
|
if (typeof deps?.provision === 'function') {
|
|
800
|
-
await deps.provision(config, { ...agent });
|
|
799
|
+
await deps.provision(config, { ...agent }, tokenSigningKey);
|
|
801
800
|
try {
|
|
802
801
|
return readFileSync(tokenPath, 'utf8').trim();
|
|
803
802
|
}
|
|
@@ -806,14 +805,14 @@ async function ensureProvisioned(config, agent, relayfileRoot, projectDir, token
|
|
|
806
805
|
}
|
|
807
806
|
}
|
|
808
807
|
if (typeof deps?.provisionAgentToken === 'function') {
|
|
809
|
-
const generated = await deps.provisionAgentToken({ config, agent, tokenPath });
|
|
808
|
+
const generated = await deps.provisionAgentToken({ config, agent, tokenPath, tokenSigningKey });
|
|
810
809
|
if (typeof generated === 'string' && generated.trim()) {
|
|
811
810
|
const generatedToken = generated.trim();
|
|
812
811
|
writeFileSync(tokenPath, `${generatedToken}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
813
812
|
return generatedToken;
|
|
814
813
|
}
|
|
815
814
|
}
|
|
816
|
-
const generatedToken = generateTokenFromScript(config, agent, log, error);
|
|
815
|
+
const generatedToken = generateTokenFromScript(config, agent, tokenSigningKey, log, error);
|
|
817
816
|
if (generatedToken) {
|
|
818
817
|
ensureDirectory(path.dirname(tokenPath));
|
|
819
818
|
writeFileSync(tokenPath, `${generatedToken}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
@@ -821,7 +820,7 @@ async function ensureProvisioned(config, agent, relayfileRoot, projectDir, token
|
|
|
821
820
|
}
|
|
822
821
|
throw new Error(`missing token for ${agent.name}: ${tokenPath}. Run provisioning before launching relay.`);
|
|
823
822
|
}
|
|
824
|
-
async function ensureServices(authBase, fileBase, deps, log, error) {
|
|
823
|
+
async function ensureServices(authBase, fileBase, jwksUrl, deps, log, error) {
|
|
825
824
|
const needsLocalAuth = isLocalBaseUrl(authBase);
|
|
826
825
|
const needsLocalFile = isLocalBaseUrl(fileBase);
|
|
827
826
|
if (!needsLocalAuth && !needsLocalFile) {
|
|
@@ -832,14 +831,14 @@ async function ensureServices(authBase, fileBase, deps, log, error) {
|
|
|
832
831
|
if (authHealthy && fileHealthy)
|
|
833
832
|
return;
|
|
834
833
|
if (typeof deps?.ensureServicesRunning === 'function') {
|
|
835
|
-
await deps.ensureServicesRunning(authBase, fileBase);
|
|
834
|
+
await deps.ensureServicesRunning(authBase, fileBase, jwksUrl);
|
|
836
835
|
const postAuthHealthy = !needsLocalAuth || (await waitForHttpHealthy(authBase));
|
|
837
836
|
const postFileHealthy = !needsLocalFile || (await waitForHttpHealthy(fileBase));
|
|
838
837
|
if (postAuthHealthy && postFileHealthy)
|
|
839
838
|
return;
|
|
840
839
|
}
|
|
841
840
|
if (typeof deps?.startServices === 'function') {
|
|
842
|
-
await deps.startServices({ authBase, fileBase });
|
|
841
|
+
await deps.startServices({ authBase, fileBase, jwksUrl });
|
|
843
842
|
const postAuthHealthy = !needsLocalAuth || (await waitForHttpHealthy(authBase));
|
|
844
843
|
const postFileHealthy = !needsLocalFile || (await waitForHttpHealthy(fileBase));
|
|
845
844
|
if (postAuthHealthy && postFileHealthy)
|
|
@@ -887,297 +886,311 @@ export async function goOnTheRelay(cli, options, extraArgs, deps = {}) {
|
|
|
887
886
|
if (!isCommandAvailable('node') || !isCommandAvailable('npx')) {
|
|
888
887
|
throw new Error('node and npx must be available in PATH to run relay.');
|
|
889
888
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
relayDir,
|
|
908
|
-
relaycastBaseUrl: process.env.RELAYCAST_BASE_URL,
|
|
909
|
-
fetchFn: deps.fetch,
|
|
910
|
-
preferLocalSession: useSymlinkMount,
|
|
911
|
-
});
|
|
912
|
-
// Compile dotfile permissions for this agent
|
|
913
|
-
const hasDots = hasDotfiles(projectDir);
|
|
914
|
-
const dotfileAcl = hasDots ? compileDotfiles(projectDir, agent.name, workspaceSession.workspaceId) : null;
|
|
915
|
-
const compiledPath = path.join(relayDir, 'compiled-acl.json');
|
|
916
|
-
const compiled = extractPermissionPatternsFromCompiled(compiledPath, agent.name);
|
|
917
|
-
const fallback = collectPermissionPatternsFromDotfiles(projectDir);
|
|
918
|
-
const readonlyPatterns = compiled.readonlyPatterns.length > 0 ? compiled.readonlyPatterns : fallback.readonlyPatterns;
|
|
919
|
-
const ignoredPatterns = compiled.ignoredPatterns.length > 0 ? compiled.ignoredPatterns : fallback.ignoredPatterns;
|
|
920
|
-
const permsDoc = buildPermissionDoc(agent.name, readonlyPatterns, ignoredPatterns);
|
|
921
|
-
const seedExcludes = [...DEFAULT_SEED_EXCLUDES];
|
|
922
|
-
if (dotfileAcl) {
|
|
923
|
-
for (const [dir, rules] of Object.entries(dotfileAcl.acl)) {
|
|
924
|
-
if (rules.some((r) => r.startsWith('deny:agent:'))) {
|
|
925
|
-
seedExcludes.push(dir.replace(/^\//, ''));
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
const mountDirName = `workspace-${sanitizePathComponent(workspaceSession.workspaceId)}-${sanitizePathComponent(agent.name)}`;
|
|
930
|
-
// Symlink mounts live under ~/.agent-relay/mounts/ (outside the project tree).
|
|
931
|
-
// @relayfile/local-mount refuses any mountDir that overlaps projectDir as a
|
|
932
|
-
// safety check against destroying the project on cleanup, and putting mounts
|
|
933
|
-
// in $HOME keeps them durable across reboots (unlike $TMPDIR) and scoped to
|
|
934
|
-
// the user, consistent with ~/.agent-workforce/. The FUSE path keeps the
|
|
935
|
-
// historical in-project location under .relay/ — it's managed by the
|
|
936
|
-
// relayfile-mount Go binary, not @relayfile/local-mount, so the overlap
|
|
937
|
-
// guard doesn't apply.
|
|
938
|
-
const mountDir = useSymlinkMount
|
|
939
|
-
? path.join(os.homedir(), '.agent-relay', 'mounts', mountDirName)
|
|
940
|
-
: path.join(relayDir, mountDirName);
|
|
941
|
-
const sandboxFlags = getSandboxFlags(cli);
|
|
942
|
-
const buildAgentEnv = () => ({
|
|
943
|
-
RELAY_AGENT_TOKEN: workspaceSession.token,
|
|
944
|
-
RELAYFILE_TOKEN: workspaceSession.token,
|
|
945
|
-
RELAYFILE_BASE_URL: workspaceSession.relayfileUrl,
|
|
946
|
-
RELAYFILE_WORKSPACE: workspaceSession.workspaceId,
|
|
947
|
-
RELAY_WORKSPACE_ID: workspaceSession.workspaceId,
|
|
948
|
-
RELAY_DEFAULT_WORKSPACE: workspaceSession.workspaceId,
|
|
949
|
-
RELAY_WORKSPACE: mountDir,
|
|
950
|
-
RELAY_AGENT_NAME: agent.name,
|
|
951
|
-
...(workspaceSession.relaycastApiKey
|
|
952
|
-
? {
|
|
953
|
-
RELAY_API_KEY: workspaceSession.relaycastApiKey,
|
|
954
|
-
RELAY_WORKSPACES_JSON: JSON.stringify([
|
|
955
|
-
{
|
|
956
|
-
workspace_id: workspaceSession.workspaceId,
|
|
957
|
-
api_key: workspaceSession.relaycastApiKey,
|
|
958
|
-
},
|
|
959
|
-
]),
|
|
960
|
-
}
|
|
961
|
-
: {}),
|
|
962
|
-
});
|
|
963
|
-
if (useSymlinkMount) {
|
|
964
|
-
log(`Preparing local workspace at ${mountDir}...`);
|
|
965
|
-
const agentArgs = [...sandboxFlags, ...extraArgs];
|
|
966
|
-
// Extend ignoredPatterns with `_PERMISSIONS.md` so @relayfile/local-mount's
|
|
967
|
-
// syncBack() does not copy the permissions doc we write in onBeforeLaunch
|
|
968
|
-
// into the user's project directory (the library only hides its own
|
|
969
|
-
// _MOUNT_README.md / .relayfile-local-mount marker from sync-back).
|
|
970
|
-
const launchIgnoredPatterns = [...ignoredPatterns, '_PERMISSIONS.md'];
|
|
971
|
-
// Ensure `.relay` is excluded from the mount — @relayfile/local-mount no
|
|
972
|
-
// longer has it in the default excludeDirs list, and seedExcludes already
|
|
973
|
-
// includes it for symlink + cloud paths.
|
|
974
|
-
const launchResult = await launchOnMount({
|
|
975
|
-
cli,
|
|
976
|
-
projectDir,
|
|
977
|
-
mountDir,
|
|
978
|
-
args: agentArgs,
|
|
979
|
-
ignoredPatterns: launchIgnoredPatterns,
|
|
980
|
-
readonlyPatterns,
|
|
981
|
-
excludeDirs: seedExcludes,
|
|
982
|
-
env: { ...process.env, ...buildAgentEnv() },
|
|
889
|
+
const localJwks = await createLocalJwks();
|
|
890
|
+
try {
|
|
891
|
+
const privateKeyPem = exportPrivateKeyPem(localJwks.privateKey);
|
|
892
|
+
ensureStateDirs(relayDir);
|
|
893
|
+
const defaultAgentName = toString(options.agent, path.basename(cli));
|
|
894
|
+
const config = resolveConfig(projectDir, relayDir, defaultAgentName);
|
|
895
|
+
const agent = findAgentConfig(config, defaultAgentName);
|
|
896
|
+
const authBase = normalizeBaseUrl(options.cloud && options.url ? options.url : options.portAuth);
|
|
897
|
+
const fileBase = normalizeBaseUrl(options.portFile);
|
|
898
|
+
// Default: solo local (symlink mount). --shared or --cloud: use relayfile.
|
|
899
|
+
const useSymlinkMount = !options.shared && !options.cloud;
|
|
900
|
+
await ensureServices(authBase, fileBase, localJwks.jwksUrl, deps, log, error);
|
|
901
|
+
const workspaceSession = await requestWorkspaceSession({
|
|
902
|
+
authBase,
|
|
903
|
+
fallbackRelayfileUrl: fileBase,
|
|
904
|
+
requestedWorkspaceId: options.workspace,
|
|
905
|
+
workspaceName: config.workspace,
|
|
983
906
|
agentName: agent.name,
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
log(` Workspace: ${workspaceSession.workspaceId}`);
|
|
991
|
-
log(` Join: ${workspaceSession.joinCommand}`);
|
|
992
|
-
log(` Mounted files: ${mountedFiles}`);
|
|
993
|
-
log(` Permissions denied (initial sync): 0`);
|
|
994
|
-
if (sandboxFlags.length > 0) {
|
|
995
|
-
log(` Sandbox: relay-enforced (${sandboxFlags.join(' ')})`);
|
|
996
|
-
log(` ⚠ Agent CLI sandbox bypassed — relay file permissions are the only safety layer`);
|
|
997
|
-
}
|
|
998
|
-
},
|
|
999
|
-
onAfterSync: (synced) => {
|
|
1000
|
-
log(` ✓ ${synced} file(s) synced back`);
|
|
1001
|
-
log(`Cleaned relay mount for ${agent.name}`);
|
|
1002
|
-
},
|
|
907
|
+
scopes: agent.scopes,
|
|
908
|
+
tokenSigningKey: localJwks,
|
|
909
|
+
relayDir,
|
|
910
|
+
relaycastBaseUrl: process.env.RELAYCAST_BASE_URL,
|
|
911
|
+
fetchFn: deps.fetch,
|
|
912
|
+
preferLocalSession: useSymlinkMount,
|
|
1003
913
|
});
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
:
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
workspace: workspaceSession.workspaceId,
|
|
1021
|
-
acl: dotfileAcl.acl,
|
|
1022
|
-
summary: dotfileAcl.summary,
|
|
1023
|
-
agents: [{ name: agent.name, summary: dotfileAcl.summary }],
|
|
1024
|
-
}, null, 2) + '\n', { encoding: 'utf8' });
|
|
914
|
+
// Compile dotfile permissions for this agent
|
|
915
|
+
const hasDots = hasDotfiles(projectDir);
|
|
916
|
+
const dotfileAcl = hasDots ? compileDotfiles(projectDir, agent.name, workspaceSession.workspaceId) : null;
|
|
917
|
+
const compiledPath = path.join(relayDir, 'compiled-acl.json');
|
|
918
|
+
const compiled = extractPermissionPatternsFromCompiled(compiledPath, agent.name);
|
|
919
|
+
const fallback = collectPermissionPatternsFromDotfiles(projectDir);
|
|
920
|
+
const readonlyPatterns = compiled.readonlyPatterns.length > 0 ? compiled.readonlyPatterns : fallback.readonlyPatterns;
|
|
921
|
+
const ignoredPatterns = compiled.ignoredPatterns.length > 0 ? compiled.ignoredPatterns : fallback.ignoredPatterns;
|
|
922
|
+
const permsDoc = buildPermissionDoc(agent.name, readonlyPatterns, ignoredPatterns);
|
|
923
|
+
const seedExcludes = [...DEFAULT_SEED_EXCLUDES];
|
|
924
|
+
if (dotfileAcl) {
|
|
925
|
+
for (const [dir, rules] of Object.entries(dotfileAcl.acl)) {
|
|
926
|
+
if (rules.some((r) => r.startsWith('deny:agent:'))) {
|
|
927
|
+
seedExcludes.push(dir.replace(/^\//, ''));
|
|
928
|
+
}
|
|
929
|
+
}
|
|
1025
930
|
}
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
log(` Sandbox: relay-enforced (${sandboxFlags.join(' ')})`);
|
|
1063
|
-
log(` ⚠ Agent CLI sandbox bypassed — relay file permissions are the only safety layer`);
|
|
1064
|
-
}
|
|
1065
|
-
const cleanupState = {
|
|
1066
|
-
mountDir,
|
|
1067
|
-
mountLogPath,
|
|
1068
|
-
projectDir,
|
|
1069
|
-
relayDir,
|
|
1070
|
-
workspace: workspaceSession.workspaceId,
|
|
1071
|
-
readonlyPatterns,
|
|
1072
|
-
ignoredPatterns,
|
|
1073
|
-
};
|
|
1074
|
-
let mountProc;
|
|
1075
|
-
let agentProc;
|
|
1076
|
-
let cleanupDone = false;
|
|
1077
|
-
const finalizeCleanup = async () => {
|
|
1078
|
-
if (cleanupDone)
|
|
1079
|
-
return;
|
|
1080
|
-
cleanupDone = true;
|
|
1081
|
-
cleanupState.mountProc = mountProc;
|
|
1082
|
-
await cleanupRun(cleanupState, agent.name, log);
|
|
1083
|
-
};
|
|
1084
|
-
try {
|
|
1085
|
-
const mountedProc = spawn(mountBin, mountBaseArgs, {
|
|
1086
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1087
|
-
env: mountEnv,
|
|
1088
|
-
});
|
|
1089
|
-
mountProc = mountedProc;
|
|
1090
|
-
mountedProc.stdout.on('data', (chunk) => {
|
|
1091
|
-
appendFileSync(mountLogPath, chunk);
|
|
1092
|
-
});
|
|
1093
|
-
mountedProc.stderr.on('data', (chunk) => {
|
|
1094
|
-
appendFileSync(mountLogPath, chunk);
|
|
931
|
+
const mountDirName = `workspace-${sanitizePathComponent(workspaceSession.workspaceId)}-${sanitizePathComponent(agent.name)}`;
|
|
932
|
+
// Symlink mounts live under ~/.agent-relay/mounts/ (outside the project tree).
|
|
933
|
+
// @relayfile/local-mount refuses any mountDir that overlaps projectDir as a
|
|
934
|
+
// safety check against destroying the project on cleanup, and putting mounts
|
|
935
|
+
// in $HOME keeps them durable across reboots (unlike $TMPDIR) and scoped to
|
|
936
|
+
// the user, consistent with ~/.agent-workforce/. The FUSE path keeps the
|
|
937
|
+
// historical in-project location under .relay/ — it's managed by the
|
|
938
|
+
// relayfile-mount Go binary, not @relayfile/local-mount, so the overlap
|
|
939
|
+
// guard doesn't apply.
|
|
940
|
+
const mountDir = useSymlinkMount
|
|
941
|
+
? path.join(os.homedir(), '.agent-relay', 'mounts', mountDirName)
|
|
942
|
+
: path.join(relayDir, mountDirName);
|
|
943
|
+
const sandboxFlags = getSandboxFlags(cli);
|
|
944
|
+
const buildAgentEnv = () => ({
|
|
945
|
+
RELAY_AGENT_TOKEN: workspaceSession.token,
|
|
946
|
+
RELAYFILE_TOKEN: workspaceSession.token,
|
|
947
|
+
RELAYFILE_BASE_URL: workspaceSession.relayfileUrl,
|
|
948
|
+
RELAYFILE_WORKSPACE: workspaceSession.workspaceId,
|
|
949
|
+
[RELAYAUTH_JWKS_URL_ENV]: localJwks.jwksUrl,
|
|
950
|
+
[RELAYAUTH_JWT_PRIVATE_KEY_PEM_ENV]: privateKeyPem,
|
|
951
|
+
[RELAYAUTH_JWT_KID_ENV]: localJwks.kid,
|
|
952
|
+
RELAY_WORKSPACE_ID: workspaceSession.workspaceId,
|
|
953
|
+
RELAY_DEFAULT_WORKSPACE: workspaceSession.workspaceId,
|
|
954
|
+
RELAY_WORKSPACE: mountDir,
|
|
955
|
+
RELAY_AGENT_NAME: agent.name,
|
|
956
|
+
...(workspaceSession.relaycastApiKey
|
|
957
|
+
? {
|
|
958
|
+
RELAY_API_KEY: workspaceSession.relaycastApiKey,
|
|
959
|
+
RELAY_WORKSPACES_JSON: JSON.stringify([
|
|
960
|
+
{
|
|
961
|
+
workspace_id: workspaceSession.workspaceId,
|
|
962
|
+
api_key: workspaceSession.relaycastApiKey,
|
|
963
|
+
},
|
|
964
|
+
]),
|
|
965
|
+
}
|
|
966
|
+
: {}),
|
|
1095
967
|
});
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
968
|
+
if (useSymlinkMount) {
|
|
969
|
+
log(`Preparing local workspace at ${mountDir}...`);
|
|
970
|
+
const agentArgs = [...sandboxFlags, ...extraArgs];
|
|
971
|
+
// Extend ignoredPatterns with `_PERMISSIONS.md` so @relayfile/local-mount's
|
|
972
|
+
// syncBack() does not copy the permissions doc we write in onBeforeLaunch
|
|
973
|
+
// into the user's project directory (the library only hides its own
|
|
974
|
+
// _MOUNT_README.md / .relayfile-local-mount marker from sync-back).
|
|
975
|
+
const launchIgnoredPatterns = [...ignoredPatterns, '_PERMISSIONS.md'];
|
|
976
|
+
// Ensure `.relay` is excluded from the mount — @relayfile/local-mount no
|
|
977
|
+
// longer has it in the default excludeDirs list, and seedExcludes already
|
|
978
|
+
// includes it for symlink + cloud paths.
|
|
979
|
+
const launchResult = await launchOnMount({
|
|
980
|
+
cli,
|
|
981
|
+
projectDir,
|
|
982
|
+
mountDir,
|
|
983
|
+
args: agentArgs,
|
|
984
|
+
ignoredPatterns: launchIgnoredPatterns,
|
|
985
|
+
readonlyPatterns,
|
|
986
|
+
excludeDirs: seedExcludes,
|
|
987
|
+
env: { ...process.env, ...buildAgentEnv() },
|
|
988
|
+
agentName: agent.name,
|
|
989
|
+
onBeforeLaunch: (realMountDir) => {
|
|
990
|
+
// Write the richer agent-relay permissions doc. This coexists with the
|
|
991
|
+
// generic _MOUNT_README.md that @relayfile/local-mount writes itself.
|
|
992
|
+
writeFileSync(path.join(realMountDir, '_PERMISSIONS.md'), permsDoc, 'utf8');
|
|
993
|
+
const mountedFiles = countFilesForSync(realMountDir);
|
|
994
|
+
log(`On the relay as ${agent.name}`);
|
|
995
|
+
log(` Workspace: ${workspaceSession.workspaceId}`);
|
|
996
|
+
log(` Join: ${workspaceSession.joinCommand}`);
|
|
997
|
+
log(` Mounted files: ${mountedFiles}`);
|
|
998
|
+
log(` Permissions denied (initial sync): 0`);
|
|
999
|
+
if (sandboxFlags.length > 0) {
|
|
1000
|
+
log(` Sandbox: relay-enforced (${sandboxFlags.join(' ')})`);
|
|
1001
|
+
log(` ⚠ Agent CLI sandbox bypassed — relay file permissions are the only safety layer`);
|
|
1002
|
+
}
|
|
1003
|
+
},
|
|
1004
|
+
onAfterSync: (synced) => {
|
|
1005
|
+
log(` ✓ ${synced} file(s) synced back`);
|
|
1006
|
+
log(`Cleaned relay mount for ${agent.name}`);
|
|
1007
|
+
},
|
|
1105
1008
|
});
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1009
|
+
log('Off the relay.');
|
|
1010
|
+
exit(launchResult.exitCode);
|
|
1011
|
+
return;
|
|
1109
1012
|
}
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1013
|
+
// Cloud / --shared path: use the relayfile-mount FUSE binary.
|
|
1014
|
+
const mountBin = process.env.RELAYFILE_ROOT
|
|
1015
|
+
? path.join(process.env.RELAYFILE_ROOT, 'bin', 'relayfile-mount')
|
|
1016
|
+
: await ensureRelayfileMountBinary();
|
|
1017
|
+
if (!existsSync(mountBin)) {
|
|
1018
|
+
throw new Error(`missing relayfile mount binary: ${mountBin}`);
|
|
1019
|
+
}
|
|
1020
|
+
if (workspaceSession.created) {
|
|
1021
|
+
await seedWorkspace(workspaceSession.relayfileUrl, workspaceSession.token, workspaceSession.workspaceId, projectDir, seedExcludes);
|
|
1022
|
+
if (dotfileAcl && Object.keys(dotfileAcl.acl).length > 0) {
|
|
1023
|
+
await seedAclRules(workspaceSession.relayfileUrl, workspaceSession.token, workspaceSession.workspaceId, dotfileAcl.acl);
|
|
1024
|
+
writeFileSync(compiledPath, JSON.stringify({
|
|
1025
|
+
workspace: workspaceSession.workspaceId,
|
|
1026
|
+
acl: dotfileAcl.acl,
|
|
1027
|
+
summary: dotfileAcl.summary,
|
|
1028
|
+
agents: [{ name: agent.name, summary: dotfileAcl.summary }],
|
|
1029
|
+
}, null, 2) + '\n', { encoding: 'utf8' });
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
mkdirSync(mountDir, { recursive: true });
|
|
1033
|
+
const mountLogPath = path.join(relayDir, 'logs', `${agent.name}-mount.log`);
|
|
1034
|
+
writeFileSync(mountLogPath, '', 'utf8');
|
|
1035
|
+
const mountBaseArgs = [
|
|
1036
|
+
'--base-url',
|
|
1037
|
+
workspaceSession.relayfileUrl,
|
|
1038
|
+
'--workspace',
|
|
1039
|
+
workspaceSession.workspaceId,
|
|
1040
|
+
'--local-dir',
|
|
1041
|
+
mountDir,
|
|
1042
|
+
];
|
|
1043
|
+
const onceArgs = [...mountBaseArgs, '--once'];
|
|
1044
|
+
const mountEnv = {
|
|
1045
|
+
...process.env,
|
|
1046
|
+
RELAYFILE_TOKEN: workspaceSession.token,
|
|
1047
|
+
[RELAYAUTH_JWKS_URL_ENV]: localJwks.jwksUrl,
|
|
1048
|
+
};
|
|
1049
|
+
log(`Mounting workspace at ${mountDir}...`);
|
|
1050
|
+
let initialSyncOutput = '';
|
|
1051
|
+
try {
|
|
1052
|
+
initialSyncOutput = await runCommandCapture(mountBin, onceArgs, mountEnv);
|
|
1053
|
+
}
|
|
1054
|
+
catch (err) {
|
|
1055
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1056
|
+
throw new Error(`initial workspace sync failed for ${agent.name}: ${message}`);
|
|
1057
|
+
}
|
|
1058
|
+
const deniedCount = pickDeniedCount(initialSyncOutput);
|
|
1059
|
+
writeFileSync(path.join(mountDir, '_PERMISSIONS.md'), permsDoc, 'utf8');
|
|
1060
|
+
const projectDeny = path.join(projectDir, '.agentdeny');
|
|
1061
|
+
if (existsSync(projectDeny)) {
|
|
1062
|
+
cpSync(projectDeny, path.join(mountDir, '.agentdeny'), { force: true });
|
|
1063
|
+
}
|
|
1064
|
+
const mountedFiles = countFilesForSync(mountDir);
|
|
1065
|
+
log(`On the relay as ${agent.name}`);
|
|
1066
|
+
log(` Workspace: ${workspaceSession.workspaceId}`);
|
|
1067
|
+
log(` Join: ${workspaceSession.joinCommand}`);
|
|
1068
|
+
log(` Mounted files: ${mountedFiles}`);
|
|
1069
|
+
log(` Permissions denied (initial sync): ${deniedCount}`);
|
|
1070
|
+
if (sandboxFlags.length > 0) {
|
|
1071
|
+
log(` Sandbox: relay-enforced (${sandboxFlags.join(' ')})`);
|
|
1072
|
+
log(` ⚠ Agent CLI sandbox bypassed — relay file permissions are the only safety layer`);
|
|
1073
|
+
}
|
|
1074
|
+
const cleanupState = {
|
|
1075
|
+
mountDir,
|
|
1076
|
+
mountLogPath,
|
|
1077
|
+
projectDir,
|
|
1078
|
+
relayDir,
|
|
1079
|
+
workspace: workspaceSession.workspaceId,
|
|
1080
|
+
readonlyPatterns,
|
|
1081
|
+
ignoredPatterns,
|
|
1082
|
+
};
|
|
1083
|
+
let mountProc;
|
|
1084
|
+
let agentProc;
|
|
1085
|
+
let cleanupDone = false;
|
|
1086
|
+
const finalizeCleanup = async () => {
|
|
1087
|
+
if (cleanupDone)
|
|
1088
|
+
return;
|
|
1089
|
+
cleanupDone = true;
|
|
1090
|
+
cleanupState.mountProc = mountProc;
|
|
1091
|
+
await cleanupRun(cleanupState, agent.name, log);
|
|
1092
|
+
};
|
|
1093
|
+
try {
|
|
1094
|
+
const mountedProc = spawn(mountBin, mountBaseArgs, {
|
|
1095
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1096
|
+
env: mountEnv,
|
|
1121
1097
|
});
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
agentProc.kill('SIGTERM');
|
|
1126
|
-
}
|
|
1127
|
-
// Wait for the agent process to exit so agentExitCode is set by the close handler,
|
|
1128
|
-
// then ensure cleanup completes before resolving — avoids data loss from premature exit
|
|
1129
|
-
cleanupInProgress = new Promise((r) => {
|
|
1130
|
-
if (!agentProc || agentProc.exitCode !== null) {
|
|
1131
|
-
r();
|
|
1132
|
-
return;
|
|
1133
|
-
}
|
|
1134
|
-
const t = setTimeout(r, 2000);
|
|
1135
|
-
agentProc.once('close', () => {
|
|
1136
|
-
clearTimeout(t);
|
|
1137
|
-
r();
|
|
1138
|
-
});
|
|
1139
|
-
})
|
|
1140
|
-
.then(() => finalizeCleanup())
|
|
1141
|
-
.then(() => resolve());
|
|
1142
|
-
};
|
|
1143
|
-
process.once('SIGINT', cleanupHook);
|
|
1144
|
-
process.once('SIGTERM', cleanupHook);
|
|
1145
|
-
agentProc.on('error', (err) => {
|
|
1146
|
-
process.removeListener('SIGINT', cleanupHook);
|
|
1147
|
-
process.removeListener('SIGTERM', cleanupHook);
|
|
1148
|
-
reject(err);
|
|
1098
|
+
mountProc = mountedProc;
|
|
1099
|
+
mountedProc.stdout.on('data', (chunk) => {
|
|
1100
|
+
appendFileSync(mountLogPath, chunk);
|
|
1149
1101
|
});
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
}
|
|
1162
|
-
else {
|
|
1163
|
-
agentExitCode = 1;
|
|
1164
|
-
}
|
|
1165
|
-
// If cleanup was triggered by a signal, wait for it to finish
|
|
1166
|
-
if (cleanupInProgress) {
|
|
1167
|
-
cleanupInProgress.then(() => resolve());
|
|
1168
|
-
}
|
|
1169
|
-
else {
|
|
1102
|
+
mountedProc.stderr.on('data', (chunk) => {
|
|
1103
|
+
appendFileSync(mountLogPath, chunk);
|
|
1104
|
+
});
|
|
1105
|
+
await new Promise((resolve, reject) => {
|
|
1106
|
+
const timer = setTimeout(() => resolve(), 600);
|
|
1107
|
+
mountedProc.on('error', (spawnError) => {
|
|
1108
|
+
clearTimeout(timer);
|
|
1109
|
+
reject(spawnError);
|
|
1110
|
+
});
|
|
1111
|
+
mountedProc.on('spawn', () => {
|
|
1112
|
+
clearTimeout(timer);
|
|
1170
1113
|
resolve();
|
|
1171
|
-
}
|
|
1114
|
+
});
|
|
1172
1115
|
});
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1116
|
+
if (!ensureProcessRunning(mountedProc)) {
|
|
1117
|
+
throw new Error(`mount process for ${agent.name} exited before continuing`);
|
|
1118
|
+
}
|
|
1119
|
+
cleanupState.mountProc = mountProc;
|
|
1120
|
+
let agentExitCode = 0;
|
|
1121
|
+
await new Promise((resolve, reject) => {
|
|
1122
|
+
const envVars = {
|
|
1123
|
+
...process.env,
|
|
1124
|
+
...buildAgentEnv(),
|
|
1125
|
+
};
|
|
1126
|
+
agentProc = spawn(cli, [...sandboxFlags, ...extraArgs], {
|
|
1127
|
+
cwd: mountDir,
|
|
1128
|
+
stdio: 'inherit',
|
|
1129
|
+
env: envVars,
|
|
1130
|
+
});
|
|
1131
|
+
let cleanupInProgress;
|
|
1132
|
+
const cleanupHook = () => {
|
|
1133
|
+
if (agentProc && !agentProc.killed) {
|
|
1134
|
+
agentProc.kill('SIGTERM');
|
|
1135
|
+
}
|
|
1136
|
+
// Wait for the agent process to exit so agentExitCode is set by the close handler,
|
|
1137
|
+
// then ensure cleanup completes before resolving — avoids data loss from premature exit
|
|
1138
|
+
cleanupInProgress = new Promise((r) => {
|
|
1139
|
+
if (!agentProc || agentProc.exitCode !== null) {
|
|
1140
|
+
r();
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
const t = setTimeout(r, 2000);
|
|
1144
|
+
agentProc.once('close', () => {
|
|
1145
|
+
clearTimeout(t);
|
|
1146
|
+
r();
|
|
1147
|
+
});
|
|
1148
|
+
})
|
|
1149
|
+
.then(() => finalizeCleanup())
|
|
1150
|
+
.then(() => resolve());
|
|
1151
|
+
};
|
|
1152
|
+
process.once('SIGINT', cleanupHook);
|
|
1153
|
+
process.once('SIGTERM', cleanupHook);
|
|
1154
|
+
agentProc.on('error', (err) => {
|
|
1155
|
+
process.removeListener('SIGINT', cleanupHook);
|
|
1156
|
+
process.removeListener('SIGTERM', cleanupHook);
|
|
1157
|
+
reject(err);
|
|
1158
|
+
});
|
|
1159
|
+
agentProc.on('close', (code, signal) => {
|
|
1160
|
+
process.removeListener('SIGINT', cleanupHook);
|
|
1161
|
+
process.removeListener('SIGTERM', cleanupHook);
|
|
1162
|
+
if (typeof code === 'number') {
|
|
1163
|
+
agentExitCode = code;
|
|
1164
|
+
}
|
|
1165
|
+
else if (signal === 'SIGINT') {
|
|
1166
|
+
agentExitCode = 130;
|
|
1167
|
+
}
|
|
1168
|
+
else if (signal === 'SIGTERM') {
|
|
1169
|
+
agentExitCode = 143;
|
|
1170
|
+
}
|
|
1171
|
+
else {
|
|
1172
|
+
agentExitCode = 1;
|
|
1173
|
+
}
|
|
1174
|
+
// If cleanup was triggered by a signal, wait for it to finish
|
|
1175
|
+
if (cleanupInProgress) {
|
|
1176
|
+
cleanupInProgress.then(() => resolve());
|
|
1177
|
+
}
|
|
1178
|
+
else {
|
|
1179
|
+
resolve();
|
|
1180
|
+
}
|
|
1181
|
+
});
|
|
1182
|
+
// Finalization happens in outer finally.
|
|
1183
|
+
});
|
|
1184
|
+
await finalizeCleanup();
|
|
1185
|
+
log('Off the relay.');
|
|
1186
|
+
exit(agentExitCode);
|
|
1187
|
+
}
|
|
1188
|
+
finally {
|
|
1189
|
+
await finalizeCleanup();
|
|
1190
|
+
}
|
|
1178
1191
|
}
|
|
1179
1192
|
finally {
|
|
1180
|
-
await
|
|
1193
|
+
await localJwks.shutdown();
|
|
1181
1194
|
}
|
|
1182
1195
|
}
|
|
1183
1196
|
//# sourceMappingURL=start.js.map
|