@enactprotocol/execution 2.2.4 → 2.3.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/docker-provider.d.ts +87 -0
- package/dist/docker-provider.d.ts.map +1 -0
- package/dist/docker-provider.js +406 -0
- package/dist/docker-provider.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/local-provider.d.ts +95 -0
- package/dist/local-provider.d.ts.map +1 -0
- package/dist/local-provider.js +369 -0
- package/dist/local-provider.js.map +1 -0
- package/dist/provider.d.ts +24 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +305 -20
- package/dist/provider.js.map +1 -1
- package/dist/remote-provider.d.ts +43 -0
- package/dist/remote-provider.d.ts.map +1 -0
- package/dist/remote-provider.js +154 -0
- package/dist/remote-provider.js.map +1 -0
- package/dist/router.d.ts +62 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +109 -0
- package/dist/router.js.map +1 -0
- package/package.json +2 -2
- package/src/docker-provider.ts +575 -0
- package/src/index.ts +32 -1
- package/src/local-provider.ts +513 -0
- package/src/provider.ts +409 -28
- package/src/remote-provider.ts +231 -0
- package/src/router.ts +143 -0
- package/tests/docker-provider.test.ts +207 -0
- package/tests/local-provider.test.ts +256 -0
- package/tests/remote-provider.test.ts +206 -0
- package/tests/router.test.ts +272 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the Remote execution provider.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import { RemoteExecutionProvider } from "../src/remote-provider";
|
|
7
|
+
|
|
8
|
+
describe("RemoteExecutionProvider", () => {
|
|
9
|
+
describe("instantiation", () => {
|
|
10
|
+
test("creates with required endpoint", () => {
|
|
11
|
+
const provider = new RemoteExecutionProvider({
|
|
12
|
+
endpoint: "http://localhost:3000",
|
|
13
|
+
});
|
|
14
|
+
expect(provider.name).toBe("remote");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("creates with full config", () => {
|
|
18
|
+
const provider = new RemoteExecutionProvider({
|
|
19
|
+
endpoint: "https://run.enact.tools",
|
|
20
|
+
authToken: "my-token",
|
|
21
|
+
defaultTimeout: 60000,
|
|
22
|
+
});
|
|
23
|
+
expect(provider.name).toBe("remote");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("strips trailing slash from endpoint", () => {
|
|
27
|
+
const provider = new RemoteExecutionProvider({
|
|
28
|
+
endpoint: "http://localhost:3000/",
|
|
29
|
+
});
|
|
30
|
+
// We verify indirectly — no trailing slash in requests
|
|
31
|
+
expect(provider.name).toBe("remote");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("interface compliance", () => {
|
|
36
|
+
test("implements all ExecutionProvider methods", () => {
|
|
37
|
+
const provider = new RemoteExecutionProvider({
|
|
38
|
+
endpoint: "http://localhost:9999",
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(typeof provider.initialize).toBe("function");
|
|
42
|
+
expect(typeof provider.isAvailable).toBe("function");
|
|
43
|
+
expect(typeof provider.getHealth).toBe("function");
|
|
44
|
+
expect(typeof provider.execute).toBe("function");
|
|
45
|
+
expect(typeof provider.exec).toBe("function");
|
|
46
|
+
expect(typeof provider.executeAction).toBe("function");
|
|
47
|
+
expect(typeof provider.shutdown).toBe("function");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("initialize resolves without error", async () => {
|
|
51
|
+
const provider = new RemoteExecutionProvider({
|
|
52
|
+
endpoint: "http://localhost:9999",
|
|
53
|
+
});
|
|
54
|
+
await expect(provider.initialize()).resolves.toBeUndefined();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("shutdown resolves without error", async () => {
|
|
58
|
+
const provider = new RemoteExecutionProvider({
|
|
59
|
+
endpoint: "http://localhost:9999",
|
|
60
|
+
});
|
|
61
|
+
await expect(provider.shutdown()).resolves.toBeUndefined();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("availability", () => {
|
|
66
|
+
test("isAvailable returns false for unreachable endpoint", async () => {
|
|
67
|
+
const provider = new RemoteExecutionProvider({
|
|
68
|
+
endpoint: "http://localhost:19999", // unlikely to be running
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const available = await provider.isAvailable();
|
|
72
|
+
expect(available).toBe(false);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("getHealth returns unhealthy for unreachable endpoint", async () => {
|
|
76
|
+
const provider = new RemoteExecutionProvider({
|
|
77
|
+
endpoint: "http://localhost:19999",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const health = await provider.getHealth();
|
|
81
|
+
expect(health.healthy).toBe(false);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("execute error handling", () => {
|
|
86
|
+
test("returns error result for unreachable endpoint", async () => {
|
|
87
|
+
const provider = new RemoteExecutionProvider({
|
|
88
|
+
endpoint: "http://localhost:19999",
|
|
89
|
+
defaultTimeout: 2000,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const result = await provider.execute(
|
|
93
|
+
{
|
|
94
|
+
enact: "2.0.0",
|
|
95
|
+
name: "@test/tool",
|
|
96
|
+
description: "test",
|
|
97
|
+
version: "1.0.0",
|
|
98
|
+
command: "echo hello",
|
|
99
|
+
},
|
|
100
|
+
{ params: {} }
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
expect(result.success).toBe(false);
|
|
104
|
+
expect(result.error).toBeDefined();
|
|
105
|
+
expect(result.metadata.toolName).toBe("@test/tool");
|
|
106
|
+
expect(result.metadata.executionId).toMatch(/^remote-/);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("exec returns error result for unreachable endpoint", async () => {
|
|
110
|
+
const provider = new RemoteExecutionProvider({
|
|
111
|
+
endpoint: "http://localhost:19999",
|
|
112
|
+
defaultTimeout: 2000,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const result = await provider.exec(
|
|
116
|
+
{
|
|
117
|
+
enact: "2.0.0",
|
|
118
|
+
name: "@test/tool",
|
|
119
|
+
description: "test",
|
|
120
|
+
version: "1.0.0",
|
|
121
|
+
},
|
|
122
|
+
"echo hello"
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
expect(result.success).toBe(false);
|
|
126
|
+
expect(result.error).toBeDefined();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe("executeAction validation", () => {
|
|
131
|
+
test("validates inputs locally before sending to remote", async () => {
|
|
132
|
+
const provider = new RemoteExecutionProvider({
|
|
133
|
+
endpoint: "http://localhost:19999",
|
|
134
|
+
defaultTimeout: 2000,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const result = await provider.executeAction(
|
|
138
|
+
{
|
|
139
|
+
enact: "2.0.0",
|
|
140
|
+
name: "@test/tool",
|
|
141
|
+
description: "test",
|
|
142
|
+
version: "1.0.0",
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
actions: {
|
|
146
|
+
greet: {
|
|
147
|
+
description: "Greet",
|
|
148
|
+
command: ["echo", "{{name}}"],
|
|
149
|
+
inputSchema: {
|
|
150
|
+
type: "object" as const,
|
|
151
|
+
properties: {
|
|
152
|
+
name: { type: "string" as const },
|
|
153
|
+
},
|
|
154
|
+
required: ["name"],
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
"greet",
|
|
160
|
+
{
|
|
161
|
+
description: "Greet",
|
|
162
|
+
command: ["echo", "{{name}}"],
|
|
163
|
+
inputSchema: {
|
|
164
|
+
type: "object" as const,
|
|
165
|
+
properties: {
|
|
166
|
+
name: { type: "string" as const },
|
|
167
|
+
},
|
|
168
|
+
required: ["name"],
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
{ params: {} } // Missing required 'name'
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
expect(result.success).toBe(false);
|
|
175
|
+
expect(result.error?.code).toBe("VALIDATION_ERROR");
|
|
176
|
+
// Should fail locally without hitting the remote endpoint
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe("metadata", () => {
|
|
181
|
+
test("error results include timing metadata", async () => {
|
|
182
|
+
const provider = new RemoteExecutionProvider({
|
|
183
|
+
endpoint: "http://localhost:19999",
|
|
184
|
+
defaultTimeout: 2000,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const before = new Date();
|
|
188
|
+
const result = await provider.execute(
|
|
189
|
+
{
|
|
190
|
+
enact: "2.0.0",
|
|
191
|
+
name: "@test/tool",
|
|
192
|
+
description: "test",
|
|
193
|
+
version: "1.0.0",
|
|
194
|
+
command: "echo hello",
|
|
195
|
+
},
|
|
196
|
+
{ params: {} }
|
|
197
|
+
);
|
|
198
|
+
const after = new Date();
|
|
199
|
+
|
|
200
|
+
expect(result.metadata.startTime.getTime()).toBeGreaterThanOrEqual(before.getTime());
|
|
201
|
+
expect(result.metadata.endTime.getTime()).toBeLessThanOrEqual(after.getTime());
|
|
202
|
+
expect(result.metadata.durationMs).toBeGreaterThanOrEqual(0);
|
|
203
|
+
expect(result.metadata.containerImage).toBe("remote");
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
});
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the execution router — config-driven backend selection.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
import type { EngineHealth, ExecutionProvider, ExecutionResult } from "@enactprotocol/shared";
|
|
7
|
+
import { ExecutionRouter } from "../src/router";
|
|
8
|
+
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Mock providers
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
|
|
13
|
+
function mockProvider(name: string, available = true): ExecutionProvider {
|
|
14
|
+
return {
|
|
15
|
+
name,
|
|
16
|
+
initialize: async () => {
|
|
17
|
+
/* no-op */
|
|
18
|
+
},
|
|
19
|
+
isAvailable: async () => available,
|
|
20
|
+
getHealth: async (): Promise<EngineHealth> => ({
|
|
21
|
+
healthy: available,
|
|
22
|
+
runtime: "docker",
|
|
23
|
+
consecutiveFailures: 0,
|
|
24
|
+
}),
|
|
25
|
+
execute: async (): Promise<ExecutionResult> => ({
|
|
26
|
+
success: true,
|
|
27
|
+
output: { stdout: `executed by ${name}`, stderr: "", exitCode: 0 },
|
|
28
|
+
metadata: {
|
|
29
|
+
toolName: "test",
|
|
30
|
+
containerImage: name,
|
|
31
|
+
startTime: new Date(),
|
|
32
|
+
endTime: new Date(),
|
|
33
|
+
durationMs: 0,
|
|
34
|
+
cached: false,
|
|
35
|
+
executionId: "test",
|
|
36
|
+
},
|
|
37
|
+
}),
|
|
38
|
+
exec: async (): Promise<ExecutionResult> => ({
|
|
39
|
+
success: true,
|
|
40
|
+
output: { stdout: "", stderr: "", exitCode: 0 },
|
|
41
|
+
metadata: {
|
|
42
|
+
toolName: "test",
|
|
43
|
+
containerImage: name,
|
|
44
|
+
startTime: new Date(),
|
|
45
|
+
endTime: new Date(),
|
|
46
|
+
durationMs: 0,
|
|
47
|
+
cached: false,
|
|
48
|
+
executionId: "test",
|
|
49
|
+
},
|
|
50
|
+
}),
|
|
51
|
+
executeAction: async (): Promise<ExecutionResult> => ({
|
|
52
|
+
success: true,
|
|
53
|
+
output: { stdout: "", stderr: "", exitCode: 0 },
|
|
54
|
+
metadata: {
|
|
55
|
+
toolName: "test",
|
|
56
|
+
containerImage: name,
|
|
57
|
+
startTime: new Date(),
|
|
58
|
+
endTime: new Date(),
|
|
59
|
+
durationMs: 0,
|
|
60
|
+
cached: false,
|
|
61
|
+
executionId: "test",
|
|
62
|
+
},
|
|
63
|
+
}),
|
|
64
|
+
shutdown: async () => {
|
|
65
|
+
/* no-op */
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Tests
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
describe("ExecutionRouter", () => {
|
|
75
|
+
describe("provider registration", () => {
|
|
76
|
+
test("registers and selects a provider by name", async () => {
|
|
77
|
+
const router = new ExecutionRouter({ default: "local" });
|
|
78
|
+
router.registerProvider("local", mockProvider("local"));
|
|
79
|
+
|
|
80
|
+
const provider = await router.selectProvider("@test/tool");
|
|
81
|
+
expect(provider.name).toBe("local");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("supports multiple providers", async () => {
|
|
85
|
+
const router = new ExecutionRouter({ default: "docker" });
|
|
86
|
+
router.registerProvider("local", mockProvider("local"));
|
|
87
|
+
router.registerProvider("docker", mockProvider("docker"));
|
|
88
|
+
|
|
89
|
+
const provider = await router.selectProvider("@test/tool");
|
|
90
|
+
expect(provider.name).toBe("docker");
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe("default backend selection", () => {
|
|
95
|
+
test("uses configured default backend", async () => {
|
|
96
|
+
const router = new ExecutionRouter({ default: "docker" });
|
|
97
|
+
router.registerProvider("local", mockProvider("local"));
|
|
98
|
+
router.registerProvider("docker", mockProvider("docker"));
|
|
99
|
+
|
|
100
|
+
const provider = await router.selectProvider("@test/tool");
|
|
101
|
+
expect(provider.name).toBe("docker");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("falls back to 'container' alias when no default set", async () => {
|
|
105
|
+
const router = new ExecutionRouter();
|
|
106
|
+
router.registerProvider("local", mockProvider("local"));
|
|
107
|
+
router.registerProvider("docker", mockProvider("docker"));
|
|
108
|
+
|
|
109
|
+
// Default is "container" which resolves to docker
|
|
110
|
+
const provider = await router.selectProvider("@test/tool");
|
|
111
|
+
expect(provider.name).toBe("docker");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("container alias tries docker first, then dagger", async () => {
|
|
115
|
+
const router = new ExecutionRouter({ default: "container" });
|
|
116
|
+
// Docker unavailable, dagger available
|
|
117
|
+
router.registerProvider("docker", mockProvider("docker", false));
|
|
118
|
+
router.registerProvider("dagger", mockProvider("dagger", true));
|
|
119
|
+
router.registerProvider("local", mockProvider("local"));
|
|
120
|
+
|
|
121
|
+
const provider = await router.selectProvider("@test/tool");
|
|
122
|
+
expect(provider.name).toBe("dagger");
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe("fallback behavior", () => {
|
|
127
|
+
test("falls back when default is unavailable", async () => {
|
|
128
|
+
const router = new ExecutionRouter({
|
|
129
|
+
default: "docker",
|
|
130
|
+
fallback: "local",
|
|
131
|
+
});
|
|
132
|
+
router.registerProvider("docker", mockProvider("docker", false));
|
|
133
|
+
router.registerProvider("local", mockProvider("local"));
|
|
134
|
+
|
|
135
|
+
const provider = await router.selectProvider("@test/tool");
|
|
136
|
+
expect(provider.name).toBe("local");
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("falls to local as last resort when both default and fallback unavailable", async () => {
|
|
140
|
+
const router = new ExecutionRouter({
|
|
141
|
+
default: "docker",
|
|
142
|
+
fallback: "remote",
|
|
143
|
+
});
|
|
144
|
+
router.registerProvider("docker", mockProvider("docker", false));
|
|
145
|
+
router.registerProvider("remote", mockProvider("remote", false));
|
|
146
|
+
router.registerProvider("local", mockProvider("local"));
|
|
147
|
+
|
|
148
|
+
const provider = await router.selectProvider("@test/tool");
|
|
149
|
+
expect(provider.name).toBe("local");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("throws when no providers are available", async () => {
|
|
153
|
+
const router = new ExecutionRouter({ default: "docker" });
|
|
154
|
+
router.registerProvider("docker", mockProvider("docker", false));
|
|
155
|
+
|
|
156
|
+
await expect(router.selectProvider("@test/tool")).rejects.toThrow(
|
|
157
|
+
"No execution provider available"
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
describe("trusted_scopes", () => {
|
|
163
|
+
test("uses local for tools matching trusted scope", async () => {
|
|
164
|
+
const router = new ExecutionRouter({
|
|
165
|
+
default: "docker",
|
|
166
|
+
trusted_scopes: ["@my-org/*"],
|
|
167
|
+
});
|
|
168
|
+
router.registerProvider("local", mockProvider("local"));
|
|
169
|
+
router.registerProvider("docker", mockProvider("docker"));
|
|
170
|
+
|
|
171
|
+
const provider = await router.selectProvider("@my-org/some-tool");
|
|
172
|
+
expect(provider.name).toBe("local");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("uses default for tools NOT matching trusted scope", async () => {
|
|
176
|
+
const router = new ExecutionRouter({
|
|
177
|
+
default: "docker",
|
|
178
|
+
trusted_scopes: ["@my-org/*"],
|
|
179
|
+
});
|
|
180
|
+
router.registerProvider("local", mockProvider("local"));
|
|
181
|
+
router.registerProvider("docker", mockProvider("docker"));
|
|
182
|
+
|
|
183
|
+
const provider = await router.selectProvider("@other-org/tool");
|
|
184
|
+
expect(provider.name).toBe("docker");
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("supports exact match in trusted_scopes", async () => {
|
|
188
|
+
const router = new ExecutionRouter({
|
|
189
|
+
default: "docker",
|
|
190
|
+
trusted_scopes: ["@specific/tool"],
|
|
191
|
+
});
|
|
192
|
+
router.registerProvider("local", mockProvider("local"));
|
|
193
|
+
router.registerProvider("docker", mockProvider("docker"));
|
|
194
|
+
|
|
195
|
+
const exact = await router.selectProvider("@specific/tool");
|
|
196
|
+
expect(exact.name).toBe("local");
|
|
197
|
+
|
|
198
|
+
const other = await router.selectProvider("@specific/other");
|
|
199
|
+
expect(other.name).toBe("docker");
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("supports multiple trusted_scopes", async () => {
|
|
203
|
+
const router = new ExecutionRouter({
|
|
204
|
+
default: "docker",
|
|
205
|
+
trusted_scopes: ["@org-a/*", "@org-b/*"],
|
|
206
|
+
});
|
|
207
|
+
router.registerProvider("local", mockProvider("local"));
|
|
208
|
+
router.registerProvider("docker", mockProvider("docker"));
|
|
209
|
+
|
|
210
|
+
const a = await router.selectProvider("@org-a/tool");
|
|
211
|
+
expect(a.name).toBe("local");
|
|
212
|
+
|
|
213
|
+
const b = await router.selectProvider("@org-b/tool");
|
|
214
|
+
expect(b.name).toBe("local");
|
|
215
|
+
|
|
216
|
+
const c = await router.selectProvider("@org-c/tool");
|
|
217
|
+
expect(c.name).toBe("docker");
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test("empty trusted_scopes does not affect routing", async () => {
|
|
221
|
+
const router = new ExecutionRouter({
|
|
222
|
+
default: "docker",
|
|
223
|
+
trusted_scopes: [],
|
|
224
|
+
});
|
|
225
|
+
router.registerProvider("local", mockProvider("local"));
|
|
226
|
+
router.registerProvider("docker", mockProvider("docker"));
|
|
227
|
+
|
|
228
|
+
const provider = await router.selectProvider("@my-org/tool");
|
|
229
|
+
expect(provider.name).toBe("docker");
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
describe("CLI flag overrides", () => {
|
|
234
|
+
test("forceLocal overrides all routing", async () => {
|
|
235
|
+
const router = new ExecutionRouter({ default: "docker" });
|
|
236
|
+
router.registerProvider("local", mockProvider("local"));
|
|
237
|
+
router.registerProvider("docker", mockProvider("docker"));
|
|
238
|
+
|
|
239
|
+
const provider = await router.selectProvider("@test/tool", {
|
|
240
|
+
forceLocal: true,
|
|
241
|
+
});
|
|
242
|
+
expect(provider.name).toBe("local");
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("forceRemote overrides all routing", async () => {
|
|
246
|
+
const router = new ExecutionRouter({ default: "docker" });
|
|
247
|
+
router.registerProvider("local", mockProvider("local"));
|
|
248
|
+
router.registerProvider("docker", mockProvider("docker"));
|
|
249
|
+
router.registerProvider("remote", mockProvider("remote"));
|
|
250
|
+
|
|
251
|
+
const provider = await router.selectProvider("@test/tool", {
|
|
252
|
+
forceRemote: true,
|
|
253
|
+
});
|
|
254
|
+
expect(provider.name).toBe("remote");
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("forceLocal overrides even trusted_scopes", async () => {
|
|
258
|
+
const router = new ExecutionRouter({
|
|
259
|
+
default: "docker",
|
|
260
|
+
trusted_scopes: ["@my-org/*"],
|
|
261
|
+
});
|
|
262
|
+
router.registerProvider("local", mockProvider("local"));
|
|
263
|
+
router.registerProvider("docker", mockProvider("docker"));
|
|
264
|
+
|
|
265
|
+
// Even for untrusted tool, forceLocal wins
|
|
266
|
+
const provider = await router.selectProvider("@untrusted/tool", {
|
|
267
|
+
forceLocal: true,
|
|
268
|
+
});
|
|
269
|
+
expect(provider.name).toBe("local");
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
});
|