@finalai/final-agent-provider 0.10.1 → 0.11.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/HOSTING.md +334 -245
- package/README.md +126 -40
- package/WORKSPACE-EGRESS.md +11 -11
- package/dist/modules/catalog.d.mts +9 -0
- package/dist/modules/catalog.mjs +21 -0
- package/dist/modules/catalog.mjs.map +7 -0
- package/dist/modules/harnesses/acp-agent.d.mts +4 -0
- package/dist/modules/harnesses/acp-agent.mjs +35 -0
- package/dist/modules/harnesses/acp-agent.mjs.map +7 -0
- package/dist/modules/harnesses/adapters.d.mts +7 -0
- package/dist/modules/harnesses/adapters.mjs +74 -0
- package/dist/modules/harnesses/adapters.mjs.map +7 -0
- package/dist/modules/harnesses/claude-code.d.mts +14 -0
- package/dist/modules/harnesses/claude-code.mjs +79 -0
- package/dist/modules/harnesses/claude-code.mjs.map +7 -0
- package/dist/modules/harnesses/codex.d.mts +12 -0
- package/dist/modules/harnesses/codex.mjs +90 -0
- package/dist/modules/harnesses/codex.mjs.map +7 -0
- package/dist/modules/harnesses/config-fields.d.mts +98 -57
- package/dist/modules/harnesses/config-fields.mjs +58 -35
- package/dist/modules/harnesses/config-fields.mjs.map +3 -3
- package/dist/modules/harnesses/definitions.d.mts +3 -0
- package/dist/modules/harnesses/definitions.mjs +21 -0
- package/dist/modules/harnesses/definitions.mjs.map +7 -0
- package/dist/modules/harnesses/fidelity.d.mts +1 -1
- package/dist/modules/harnesses/fidelity.mjs +17 -14
- package/dist/modules/harnesses/fidelity.mjs.map +2 -2
- package/dist/modules/harnesses/final.d.mts +12 -0
- package/dist/modules/harnesses/final.mjs +70 -0
- package/dist/modules/harnesses/final.mjs.map +7 -0
- package/dist/modules/harnesses/index.d.mts +13 -12
- package/dist/modules/harnesses/index.mjs +86 -45
- package/dist/modules/harnesses/index.mjs.map +3 -3
- package/dist/modules/harnesses/names.d.mts +43 -0
- package/dist/modules/harnesses/names.mjs +59 -0
- package/dist/modules/harnesses/names.mjs.map +7 -0
- package/dist/modules/harnesses/nanocode-claude.d.mts +18 -0
- package/dist/modules/harnesses/nanocode-claude.mjs +101 -0
- package/dist/modules/harnesses/nanocode-claude.mjs.map +7 -0
- package/dist/modules/harnesses/nanocode-codex.d.mts +13 -0
- package/dist/modules/harnesses/nanocode-codex.mjs +64 -0
- package/dist/modules/harnesses/nanocode-codex.mjs.map +7 -0
- package/dist/modules/harnesses/pi.d.mts +12 -0
- package/dist/modules/harnesses/pi.mjs +71 -0
- package/dist/modules/harnesses/pi.mjs.map +7 -0
- package/dist/modules/integrations.d.mts +9 -0
- package/dist/modules/integrations.mjs +29 -0
- package/dist/modules/integrations.mjs.map +7 -0
- package/dist/modules/provider/general.bundle.d.mts +1 -0
- package/dist/modules/provider/general.bundle.mjs +5 -0
- package/dist/modules/provider/general.bundle.mjs.map +7 -0
- package/dist/modules/provider/general.d.mts +11 -0
- package/dist/modules/provider/general.mjs +130 -0
- package/dist/modules/provider/general.mjs.map +7 -0
- package/dist/modules/provider/index.d.mts +3 -0
- package/dist/modules/provider/index.mjs +13 -0
- package/dist/modules/provider/index.mjs.map +7 -0
- package/dist/modules/sandboxes/access.d.mts +34 -0
- package/dist/modules/sandboxes/access.mjs +15 -0
- package/dist/modules/sandboxes/access.mjs.map +7 -0
- package/dist/modules/sandboxes/computer.d.mts +24 -51
- package/dist/modules/sandboxes/computer.mjs +523 -888
- package/dist/modules/sandboxes/computer.mjs.map +3 -3
- package/dist/modules/sandboxes/computers-api.d.mts +3 -2
- package/dist/modules/sandboxes/computers-api.mjs +2 -4
- package/dist/modules/sandboxes/computers-api.mjs.map +2 -2
- package/dist/modules/sandboxes/device-api.d.mts +4 -3
- package/dist/modules/sandboxes/device-api.mjs +2 -4
- package/dist/modules/sandboxes/device-api.mjs.map +2 -2
- package/dist/modules/sandboxes/device.d.mts +6 -6
- package/dist/modules/sandboxes/device.mjs +126 -121
- package/dist/modules/sandboxes/device.mjs.map +3 -3
- package/dist/modules/sandboxes/egress.d.mts +10 -9
- package/dist/modules/sandboxes/egress.mjs +73 -56
- package/dist/modules/sandboxes/egress.mjs.map +2 -2
- package/dist/modules/sandboxes/environment.d.mts +35 -269
- package/dist/modules/sandboxes/environment.mjs +27 -104
- package/dist/modules/sandboxes/environment.mjs.map +3 -3
- package/dist/modules/sandboxes/index.d.mts +9 -69
- package/dist/modules/sandboxes/index.mjs +35 -95
- package/dist/modules/sandboxes/index.mjs.map +3 -3
- package/dist/modules/sandboxes/local.d.mts +28 -0
- package/dist/modules/sandboxes/local.mjs +499 -0
- package/dist/modules/sandboxes/local.mjs.map +7 -0
- package/dist/modules/sandboxes/session.d.mts +0 -3
- package/dist/modules/sandboxes/session.mjs +2 -22
- package/dist/modules/sandboxes/session.mjs.map +2 -2
- package/dist/modules/sandboxes/testing/fake-machine.mjs +0 -28
- package/dist/modules/sandboxes/testing/fake-machine.mjs.map +2 -2
- package/package.json +76 -104
- package/dist/modules/connections.d.mts +0 -11
- package/dist/modules/connections.mjs +0 -16
- package/dist/modules/connections.mjs.map +0 -7
- package/dist/modules/harnesses/acp-describe.d.mts +0 -20
- package/dist/modules/harnesses/acp-describe.mjs +0 -110
- package/dist/modules/harnesses/acp-describe.mjs.map +0 -7
- package/dist/modules/harnesses/local-stdio.d.mts +0 -47
- package/dist/modules/harnesses/local-stdio.mjs +0 -41
- package/dist/modules/harnesses/local-stdio.mjs.map +0 -7
- package/dist/modules/harnesses/remote-acp.d.mts +0 -76
- package/dist/modules/harnesses/remote-acp.mjs +0 -83
- package/dist/modules/harnesses/remote-acp.mjs.map +0 -7
- package/dist/modules/harnesses/remote-claude-code.d.mts +0 -88
- package/dist/modules/harnesses/remote-claude-code.mjs +0 -113
- package/dist/modules/harnesses/remote-claude-code.mjs.map +0 -7
- package/dist/modules/harnesses/remote-client-tls.d.mts +0 -35
- package/dist/modules/harnesses/remote-client-tls.mjs +0 -37
- package/dist/modules/harnesses/remote-client-tls.mjs.map +0 -7
- package/dist/modules/harnesses/remote-codex.d.mts +0 -98
- package/dist/modules/harnesses/remote-codex.mjs +0 -128
- package/dist/modules/harnesses/remote-codex.mjs.map +0 -7
- package/dist/modules/harnesses/remote-pi.d.mts +0 -78
- package/dist/modules/harnesses/remote-pi.mjs +0 -104
- package/dist/modules/harnesses/remote-pi.mjs.map +0 -7
- package/dist/modules/harnesses/remote-url.d.mts +0 -5
- package/dist/modules/harnesses/remote-url.mjs +0 -19
- package/dist/modules/harnesses/remote-url.mjs.map +0 -7
- package/dist/modules/harnesses/serverless-final.d.mts +0 -63
- package/dist/modules/harnesses/serverless-final.mjs +0 -88
- package/dist/modules/harnesses/serverless-final.mjs.map +0 -7
- package/dist/modules/harnesses/serverless-nanocode-claude.d.mts +0 -64
- package/dist/modules/harnesses/serverless-nanocode-claude.mjs +0 -105
- package/dist/modules/harnesses/serverless-nanocode-claude.mjs.map +0 -7
- package/dist/modules/harnesses/serverless-nanocode-codex.d.mts +0 -66
- package/dist/modules/harnesses/serverless-nanocode-codex.mjs +0 -113
- package/dist/modules/harnesses/serverless-nanocode-codex.mjs.map +0 -7
- package/dist/modules/harnesses/serverless-pi.d.mts +0 -64
- package/dist/modules/harnesses/serverless-pi.mjs +0 -98
- package/dist/modules/harnesses/serverless-pi.mjs.map +0 -7
- package/dist/modules/runtime/index.d.mts +0 -7
- package/dist/modules/runtime/index.mjs +0 -18
- package/dist/modules/runtime/index.mjs.map +0 -7
- package/dist/modules/runtime/local-stdio.d.mts +0 -10
- package/dist/modules/runtime/local-stdio.mjs +0 -106
- package/dist/modules/runtime/local-stdio.mjs.map +0 -7
- package/dist/modules/runtime/remote-acp.d.mts +0 -16
- package/dist/modules/runtime/remote-acp.mjs +0 -152
- package/dist/modules/runtime/remote-acp.mjs.map +0 -7
- package/dist/modules/runtime/remote-claude-code.d.mts +0 -5
- package/dist/modules/runtime/remote-claude-code.mjs +0 -47
- package/dist/modules/runtime/remote-claude-code.mjs.map +0 -7
- package/dist/modules/runtime/remote-codex.d.mts +0 -5
- package/dist/modules/runtime/remote-codex.mjs +0 -47
- package/dist/modules/runtime/remote-codex.mjs.map +0 -7
- package/dist/modules/runtime/remote-host.d.mts +0 -127
- package/dist/modules/runtime/remote-host.mjs +0 -387
- package/dist/modules/runtime/remote-host.mjs.map +0 -7
- package/dist/modules/runtime/remote-pi.d.mts +0 -5
- package/dist/modules/runtime/remote-pi.mjs +0 -44
- package/dist/modules/runtime/remote-pi.mjs.map +0 -7
- package/dist/modules/sandboxes/guide.d.mts +0 -35
- package/dist/modules/sandboxes/guide.mjs +0 -119
- package/dist/modules/sandboxes/guide.mjs.map +0 -7
- package/dist/modules/sandboxes/workspace-setup.d.mts +0 -42
- package/dist/modules/sandboxes/workspace-setup.mjs +0 -242
- package/dist/modules/sandboxes/workspace-setup.mjs.map +0 -7
- package/dist/modules/testing/harness.d.mts +0 -9
- package/dist/modules/testing/harness.mjs +0 -123
- package/dist/modules/testing/harness.mjs.map +0 -7
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Capability, FilesAccess, ScreenAccess, ShellAccess } from "@finalai/agent-protocol";
|
|
2
2
|
import {
|
|
3
|
-
SandboxAccess,
|
|
4
3
|
toolDescriptorSchema
|
|
5
4
|
} from "@finalai/agent-provider";
|
|
6
5
|
import {
|
|
@@ -15,19 +14,18 @@ import {
|
|
|
15
14
|
DeviceApiCapability,
|
|
16
15
|
DeviceApiLeaseAccess
|
|
17
16
|
} from "./device-api.mjs";
|
|
18
|
-
import {
|
|
17
|
+
import { DEVICE_PROVIDER, deviceEnvironmentSchema } from "./environment.mjs";
|
|
19
18
|
import { createMcpPipe } from "./mcp-pipe.mjs";
|
|
20
19
|
import { sandboxPathsFor, SandboxPlatform } from "./paths.mjs";
|
|
21
|
-
import { createTransportSession,
|
|
20
|
+
import { createTransportSession, readOnlySession } from "./session.mjs";
|
|
22
21
|
import { DEVICE_API_ACTING_USER_HEADER, DeviceApiClientErrorCode } from "./device-api.mjs";
|
|
23
|
-
import {
|
|
22
|
+
import { DEVICE_PROVIDER as DEVICE_PROVIDER2 } from "./environment.mjs";
|
|
24
23
|
import { SandboxPlatform as SandboxPlatform2 } from "./paths.mjs";
|
|
25
24
|
const deviceArtifactSchema = z.object({ version: z.literal(1), deviceId: z.string() });
|
|
26
25
|
const deviceStateSchema = z.object({
|
|
27
|
-
version: z.literal(
|
|
26
|
+
version: z.literal(2),
|
|
28
27
|
deviceId: z.string(),
|
|
29
|
-
generation: z.number().int().min(
|
|
30
|
-
access: z.nativeEnum(SandboxAccess)
|
|
28
|
+
generation: z.number().int().min(0)
|
|
31
29
|
});
|
|
32
30
|
const DEVICE_COMPUTER_USE_SERVER = DEVICE_API_COMPUTER_USE_SERVER;
|
|
33
31
|
const descriptorsOf = (tools) => tools.flatMap((tool) => {
|
|
@@ -43,6 +41,19 @@ const ONLINE_POLL_MAX_MS = 5e3;
|
|
|
43
41
|
const MAX_LEASE_TTL_MS = 60 * 6e4;
|
|
44
42
|
const MIN_LEASE_TTL_MS = 6e4;
|
|
45
43
|
const platformOf = (platform) => platform === "windows" ? SandboxPlatform.Windows : SandboxPlatform.Posix;
|
|
44
|
+
const refuseSetup = (setup) => {
|
|
45
|
+
if (setup == null) return;
|
|
46
|
+
const parts = [
|
|
47
|
+
setup.repositories.length > 0 ? "repositories" : null,
|
|
48
|
+
Object.keys(setup.variables).length > 0 ? "variables" : null,
|
|
49
|
+
setup.steps.length > 0 ? "steps" : null,
|
|
50
|
+
setup.services.length > 0 ? "services" : null,
|
|
51
|
+
Object.keys(setup.commands).length > 0 ? "commands" : null,
|
|
52
|
+
setup.network != null ? "a network policy" : null
|
|
53
|
+
].filter((part) => part != null);
|
|
54
|
+
if (parts.length > 0)
|
|
55
|
+
throw new Error(`a Final Device takes no sandbox setup (this one has ${parts.join(", ")})`);
|
|
56
|
+
};
|
|
46
57
|
const createDeviceSandboxProvider = (config) => {
|
|
47
58
|
const randomUuid = config.randomUuid ?? (() => crypto.randomUUID());
|
|
48
59
|
const now = config.now ?? (() => Date.now());
|
|
@@ -67,43 +78,48 @@ const createDeviceSandboxProvider = (config) => {
|
|
|
67
78
|
pollMs = Math.min(pollMs * 2, ONLINE_POLL_MAX_MS);
|
|
68
79
|
}
|
|
69
80
|
};
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
const credentialsFor = (ctx) => ({
|
|
82
|
+
deviceAccess: async (args) => ({
|
|
83
|
+
baseUrl: config.deviceApiUrl,
|
|
84
|
+
token: (await ctx.identity({ audience: config.deviceApiUrl, refresh: args?.refresh === true })).token
|
|
85
|
+
})
|
|
86
|
+
});
|
|
87
|
+
const callerFor = (ctx) => ({
|
|
88
|
+
organizationId: ctx.organizationId,
|
|
89
|
+
credentials: credentialsFor(ctx),
|
|
90
|
+
actingUser: ctx.actingUser
|
|
91
|
+
});
|
|
92
|
+
const lease = async (args) => {
|
|
93
|
+
const { ctx } = args;
|
|
94
|
+
const caller = callerFor(ctx);
|
|
95
|
+
const bindingId = ctx.sandboxId;
|
|
78
96
|
const device = await awaitOnline(caller, args.deviceId, onlineWaitMs);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const leaseAccess = access === SandboxAccess.Read ? DeviceApiLeaseAccess.Read : DeviceApiLeaseAccess.Write;
|
|
83
|
-
let current = args.passive === true ? await api.recoverLease({
|
|
97
|
+
const leaseAccess = device.writable ? DeviceApiLeaseAccess.Write : DeviceApiLeaseAccess.Read;
|
|
98
|
+
const afterGeneration = args.afterGeneration === 0 ? void 0 : args.afterGeneration;
|
|
99
|
+
let current = args.passive ? await api.recoverLease({
|
|
84
100
|
caller,
|
|
85
101
|
deviceId: device.id,
|
|
86
|
-
sessionId: ctx.
|
|
102
|
+
sessionId: ctx.sandboxId,
|
|
87
103
|
bindingId,
|
|
88
104
|
access: leaseAccess
|
|
89
105
|
}) : await api.lease({
|
|
90
106
|
caller,
|
|
91
107
|
deviceId: device.id,
|
|
92
|
-
sessionId: ctx.
|
|
108
|
+
sessionId: ctx.sandboxId,
|
|
93
109
|
bindingId,
|
|
94
110
|
access: leaseAccess,
|
|
95
111
|
ttlMs: leaseTtlMs,
|
|
96
|
-
afterGeneration
|
|
112
|
+
afterGeneration
|
|
97
113
|
});
|
|
98
|
-
if (args.passive
|
|
114
|
+
if (args.passive && (current.deviceId !== device.id || current.generation < args.afterGeneration || current.expiresAtMs <= now()))
|
|
99
115
|
throw new Error("The existing Device lease is unavailable");
|
|
100
116
|
let renewing = null;
|
|
101
|
-
const
|
|
117
|
+
const leaseHandle = async () => {
|
|
102
118
|
if (current.expiresAtMs - now() < LEASE_RENEW_MARGIN_MS) {
|
|
103
|
-
renewing ??= (args.passive
|
|
119
|
+
renewing ??= (args.passive ? api.recoverLease({
|
|
104
120
|
caller,
|
|
105
121
|
deviceId: device.id,
|
|
106
|
-
sessionId: ctx.
|
|
122
|
+
sessionId: ctx.sandboxId,
|
|
107
123
|
bindingId,
|
|
108
124
|
access: leaseAccess
|
|
109
125
|
}) : api.renew({ caller, lease: current.lease, ttlMs: leaseTtlMs })).finally(() => {
|
|
@@ -115,7 +131,7 @@ const createDeviceSandboxProvider = (config) => {
|
|
|
115
131
|
};
|
|
116
132
|
let released = false;
|
|
117
133
|
const release = async () => {
|
|
118
|
-
if (released) return;
|
|
134
|
+
if (released || args.passive) return;
|
|
119
135
|
released = true;
|
|
120
136
|
await api.release({ caller, lease: current.lease });
|
|
121
137
|
};
|
|
@@ -125,7 +141,7 @@ const createDeviceSandboxProvider = (config) => {
|
|
|
125
141
|
id: `${device.id}:${bindingId}`,
|
|
126
142
|
description: `${device.displayName} (Final Device)`,
|
|
127
143
|
workspaceRoot: current.workspaceRoot,
|
|
128
|
-
transport: api.transport({ caller, lease }),
|
|
144
|
+
transport: api.transport({ caller, lease: leaseHandle }),
|
|
129
145
|
baseEnv: async () => ({}),
|
|
130
146
|
randomUuid,
|
|
131
147
|
paths,
|
|
@@ -147,27 +163,15 @@ const createDeviceSandboxProvider = (config) => {
|
|
|
147
163
|
const computerUse = offered.has(DeviceApiCapability.ComputerUse);
|
|
148
164
|
const capabilities = [SandboxCapability.Files];
|
|
149
165
|
if (processes) capabilities.push(SandboxCapability.Process);
|
|
150
|
-
if (processes &&
|
|
151
|
-
capabilities.push(SandboxCapability.DurableProcesses);
|
|
166
|
+
if (processes && !args.passive) capabilities.push(SandboxCapability.DurableProcesses);
|
|
152
167
|
if (localMcp) capabilities.push(SandboxCapability.LocalMcp);
|
|
153
168
|
if (computerUse) capabilities.push(SandboxCapability.ComputerUse);
|
|
154
169
|
if (device.screenReady) capabilities.push(SandboxCapability.Screen);
|
|
155
|
-
const channelEndpoint = (server) => ({
|
|
156
|
-
name: server,
|
|
157
|
-
connect: async () => {
|
|
158
|
-
const pipe = openMcpPipe(server);
|
|
159
|
-
return {
|
|
160
|
-
send: (message, options) => pipe.send(message, options?.abortSignal),
|
|
161
|
-
receive: (options) => pipe.receive(options?.abortSignal),
|
|
162
|
-
close: () => pipe.close()
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
170
|
const openMcpPipe = (server) => {
|
|
167
171
|
const clientId = randomUuid();
|
|
168
172
|
const request = async (action, dataBase64, signal) => api.pipe({
|
|
169
173
|
caller,
|
|
170
|
-
lease: await
|
|
174
|
+
lease: await leaseHandle(),
|
|
171
175
|
server,
|
|
172
176
|
action,
|
|
173
177
|
clientId,
|
|
@@ -189,13 +193,24 @@ const createDeviceSandboxProvider = (config) => {
|
|
|
189
193
|
}
|
|
190
194
|
});
|
|
191
195
|
};
|
|
196
|
+
const channelEndpoint = (server) => ({
|
|
197
|
+
name: server,
|
|
198
|
+
connect: async () => {
|
|
199
|
+
const pipe = openMcpPipe(server);
|
|
200
|
+
return {
|
|
201
|
+
send: (message, options) => pipe.send(message, options?.abortSignal),
|
|
202
|
+
receive: (options) => pipe.receive(options?.abortSignal),
|
|
203
|
+
close: () => pipe.close()
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
});
|
|
192
207
|
const sandbox = { ...session, capabilities };
|
|
193
208
|
if (!processes) delete sandbox.attachProcess;
|
|
194
209
|
if (computerUse) {
|
|
195
210
|
sandbox.computerUse = async () => channelEndpoint(DEVICE_COMPUTER_USE_SERVER);
|
|
196
211
|
}
|
|
197
212
|
if (localMcp) {
|
|
198
|
-
sandbox.localMcp = async () => (await api.localMcpServers({ caller, lease: await
|
|
213
|
+
sandbox.localMcp = async () => (await api.localMcpServers({ caller, lease: await leaseHandle() })).map(channelEndpoint);
|
|
199
214
|
}
|
|
200
215
|
if (device.screenReady) {
|
|
201
216
|
sandbox.screen = (options) => api.screen({
|
|
@@ -209,8 +224,8 @@ const createDeviceSandboxProvider = (config) => {
|
|
|
209
224
|
return {
|
|
210
225
|
generation: current.generation,
|
|
211
226
|
handle: {
|
|
212
|
-
//
|
|
213
|
-
sandbox:
|
|
227
|
+
// A viewer's lease is the existing one: it changes nothing.
|
|
228
|
+
sandbox: args.passive ? readOnlySession(sandbox) : sandbox,
|
|
214
229
|
onSandboxStop: release,
|
|
215
230
|
onSandboxDelete: release,
|
|
216
231
|
onRuntimeShutdown: async () => void 0
|
|
@@ -223,40 +238,33 @@ const createDeviceSandboxProvider = (config) => {
|
|
|
223
238
|
const options = parsed.data;
|
|
224
239
|
const resolveDeviceId = async (caller) => {
|
|
225
240
|
if ("deviceId" in options) return options.deviceId;
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
return device.id;
|
|
230
|
-
}
|
|
231
|
-
const devices = await api.listDevices({ caller });
|
|
232
|
-
const [only] = devices;
|
|
233
|
-
if (devices.length !== 1 || only == null) {
|
|
234
|
-
throw new Error(
|
|
235
|
-
devices.length === 0 ? "the user this session acts for has no Final Device" : "the user this session acts for has several Final Devices; name one by id or pairing code"
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
return only.id;
|
|
241
|
+
const device = await api.resolvePairing({ caller, code: options.pairing });
|
|
242
|
+
if (device == null) throw new Error("the Final Device pairing code is unknown or expired");
|
|
243
|
+
return device.id;
|
|
239
244
|
};
|
|
240
245
|
return {
|
|
241
|
-
processes: async (ctx, rawState, request) => {
|
|
242
|
-
const state = deviceStateSchema.parse(rawState);
|
|
243
|
-
if (request.operation === ProcessInspectionOperation.Stop && state.access === SandboxAccess.Read)
|
|
244
|
-
throw new Error("stopping processes requires execute access");
|
|
245
|
-
return api.inspectProcesses({
|
|
246
|
-
caller: ctx,
|
|
247
|
-
deviceId: state.deviceId,
|
|
248
|
-
bindingId: ctx.bindingId,
|
|
249
|
-
request
|
|
250
|
-
});
|
|
251
|
-
},
|
|
252
246
|
// What a lease on this Device grants, known from the registry without
|
|
253
|
-
// leasing (the Device API's rule: processes
|
|
254
|
-
//
|
|
255
|
-
// driver's readiness is checked when leased.
|
|
247
|
+
// leasing (the Device API's rule: processes and computer use with a write
|
|
248
|
+
// lease on a Device that allows processes).
|
|
256
249
|
capabilities: async (ctx) => {
|
|
257
250
|
const device = await api.getDevice({
|
|
258
|
-
caller: ctx,
|
|
259
|
-
deviceId: await resolveDeviceId(ctx)
|
|
251
|
+
caller: callerFor(ctx),
|
|
252
|
+
deviceId: await resolveDeviceId(callerFor(ctx))
|
|
253
|
+
});
|
|
254
|
+
if (device == null) return {};
|
|
255
|
+
const offer = {
|
|
256
|
+
[Capability.Files]: device.writable ? FilesAccess.Write : FilesAccess.Read
|
|
257
|
+
};
|
|
258
|
+
if (device.writable && device.allowProcesses) {
|
|
259
|
+
offer[Capability.Shell] = ShellAccess.Execute;
|
|
260
|
+
offer[Capability.ComputerUse] = ScreenAccess.Control;
|
|
261
|
+
} else if (device.screenReady) offer[Capability.ComputerUse] = ScreenAccess.View;
|
|
262
|
+
return offer;
|
|
263
|
+
},
|
|
264
|
+
features: async (ctx) => {
|
|
265
|
+
const device = await api.getDevice({
|
|
266
|
+
caller: callerFor(ctx),
|
|
267
|
+
deviceId: await resolveDeviceId(callerFor(ctx))
|
|
260
268
|
});
|
|
261
269
|
if (device == null) return null;
|
|
262
270
|
const offered = [SandboxCapability.Files];
|
|
@@ -276,8 +284,8 @@ const createDeviceSandboxProvider = (config) => {
|
|
|
276
284
|
// A Device the API has never seen list gives unknown; its first lease does.
|
|
277
285
|
mcpCatalog: async (ctx) => {
|
|
278
286
|
const catalog = await api.mcpCatalog({
|
|
279
|
-
caller: ctx,
|
|
280
|
-
deviceId: await resolveDeviceId(ctx)
|
|
287
|
+
caller: callerFor(ctx),
|
|
288
|
+
deviceId: await resolveDeviceId(callerFor(ctx))
|
|
281
289
|
});
|
|
282
290
|
const local = catalog.localServers;
|
|
283
291
|
const localMcp = {};
|
|
@@ -291,78 +299,75 @@ const createDeviceSandboxProvider = (config) => {
|
|
|
291
299
|
localMcp: localKnown ? localMcp : null
|
|
292
300
|
};
|
|
293
301
|
},
|
|
302
|
+
processes: async (ctx, rawState, request) => {
|
|
303
|
+
const state = deviceStateSchema.parse(rawState);
|
|
304
|
+
return api.inspectProcesses({
|
|
305
|
+
caller: callerFor(ctx),
|
|
306
|
+
deviceId: state.deviceId,
|
|
307
|
+
bindingId: ctx.sandboxId,
|
|
308
|
+
request
|
|
309
|
+
});
|
|
310
|
+
},
|
|
311
|
+
artifactKey: async (ctx) => `device:${await resolveDeviceId(callerFor(ctx))}`,
|
|
312
|
+
prepare: async (ctx) => ({ version: 1, deviceId: await resolveDeviceId(callerFor(ctx)) }),
|
|
313
|
+
create: async (_ctx, { artifact, setup }) => {
|
|
314
|
+
refuseSetup(setup);
|
|
315
|
+
return { state: { version: 2, deviceId: artifact.deviceId, generation: 0 } };
|
|
316
|
+
},
|
|
294
317
|
awaitRunning: async (ctx, rawState, { timeoutMs }) => {
|
|
295
318
|
const { deviceId } = deviceStateSchema.pick({ deviceId: true }).parse(rawState);
|
|
296
|
-
await awaitOnline(
|
|
297
|
-
{
|
|
298
|
-
organizationId: ctx.session.organizationId,
|
|
299
|
-
credentials: ctx.credentials,
|
|
300
|
-
actingUser: ctx.actingUser
|
|
301
|
-
},
|
|
302
|
-
deviceId,
|
|
303
|
-
timeoutMs
|
|
304
|
-
);
|
|
305
|
-
},
|
|
306
|
-
artifactKey: async (ctx) => `device:${await resolveDeviceId(ctx)}`,
|
|
307
|
-
prepare: async (ctx) => ({ version: 1, deviceId: await resolveDeviceId(ctx) }),
|
|
308
|
-
start: async (ctx, openOptions, rawArtifact) => {
|
|
309
|
-
const artifact = deviceArtifactSchema.parse(rawArtifact);
|
|
310
|
-
const access = computerOpenOptionsSchema.parse(openOptions ?? {}).access;
|
|
311
|
-
const { handle, generation } = await attach({
|
|
312
|
-
ctx,
|
|
313
|
-
deviceId: artifact.deviceId,
|
|
314
|
-
afterGeneration: null,
|
|
315
|
-
access
|
|
316
|
-
});
|
|
317
|
-
return { handle, state: { version: 1, deviceId: artifact.deviceId, generation, access } };
|
|
319
|
+
await awaitOnline(callerFor(ctx), deviceId, timeoutMs);
|
|
318
320
|
},
|
|
319
321
|
// A Device never takes a lease back at an old generation once it lapsed,
|
|
320
|
-
// so every attach leases a later one.
|
|
321
|
-
|
|
322
|
+
// so every attach leases a later one, and no process from before survives.
|
|
323
|
+
attach: async (ctx, { state: rawState }) => {
|
|
322
324
|
const state = deviceStateSchema.parse(rawState);
|
|
323
|
-
|
|
325
|
+
const { handle, generation } = await lease({
|
|
324
326
|
ctx,
|
|
325
327
|
deviceId: state.deviceId,
|
|
326
328
|
afterGeneration: state.generation,
|
|
327
|
-
|
|
328
|
-
})
|
|
329
|
+
passive: false
|
|
330
|
+
});
|
|
331
|
+
return {
|
|
332
|
+
handle,
|
|
333
|
+
state: { ...state, generation },
|
|
334
|
+
fresh: state.generation === 0,
|
|
335
|
+
booted: true
|
|
336
|
+
};
|
|
329
337
|
},
|
|
330
|
-
|
|
338
|
+
attachPassive: async (ctx, { state: rawState }) => {
|
|
331
339
|
const state = deviceStateSchema.parse(rawState);
|
|
332
|
-
return (await
|
|
340
|
+
return (await lease({
|
|
333
341
|
ctx,
|
|
334
342
|
deviceId: state.deviceId,
|
|
335
343
|
afterGeneration: state.generation,
|
|
336
|
-
access: state.access,
|
|
337
344
|
passive: true
|
|
338
345
|
})).handle;
|
|
339
346
|
},
|
|
340
|
-
|
|
347
|
+
// The lease lapses on its own; the next generation retires its processes.
|
|
348
|
+
detach: async (_ctx, { state }) => deviceStateSchema.parse(state),
|
|
349
|
+
park: async (_ctx, { state }) => deviceStateSchema.parse(state),
|
|
350
|
+
// The Device stays the person's; its workspace for this sandbox is let go.
|
|
351
|
+
delete: async (ctx, { state: rawState }) => {
|
|
341
352
|
const state = deviceStateSchema.parse(rawState);
|
|
342
353
|
await api.releaseWorkspaceLeases({
|
|
343
|
-
caller:
|
|
344
|
-
organizationId: ctx.session.organizationId,
|
|
345
|
-
credentials: ctx.credentials,
|
|
346
|
-
actingUser: ctx.actingUser
|
|
347
|
-
},
|
|
354
|
+
caller: callerFor(ctx),
|
|
348
355
|
deviceId: state.deviceId,
|
|
349
|
-
bindingId: ctx.
|
|
356
|
+
bindingId: ctx.sandboxId
|
|
350
357
|
});
|
|
351
|
-
}
|
|
352
|
-
// The lease lapses on its own; the next generation retires its processes.
|
|
353
|
-
park: async (_ctx, _artifact, rawState) => deviceStateSchema.parse(rawState)
|
|
358
|
+
}
|
|
354
359
|
};
|
|
355
360
|
};
|
|
356
361
|
return defineSandboxProvider({
|
|
357
|
-
name:
|
|
358
|
-
stateProtocolVersion:
|
|
362
|
+
name: DEVICE_PROVIDER,
|
|
363
|
+
stateProtocolVersion: 2,
|
|
359
364
|
environment
|
|
360
365
|
});
|
|
361
366
|
};
|
|
362
367
|
export {
|
|
363
368
|
DEVICE_API_ACTING_USER_HEADER,
|
|
364
369
|
DEVICE_COMPUTER_USE_SERVER,
|
|
365
|
-
|
|
370
|
+
DEVICE_PROVIDER2 as DEVICE_PROVIDER,
|
|
366
371
|
DeviceApiClientErrorCode,
|
|
367
372
|
SandboxPlatform2 as SandboxPlatform,
|
|
368
373
|
createDeviceSandboxProvider
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/sandboxes/device.ts"],
|
|
4
|
-
"sourcesContent": ["import { ProcessInspectionOperation } from '@finalai/agent-protocol/process-inspection';\nimport {\n SandboxAccess,\n toolDescriptorSchema,\n type JsonObject,\n type JsonValue,\n type ToolDescriptorIface,\n} from '@finalai/agent-provider';\nimport {\n defineSandboxProvider,\n SandboxCapability,\n type McpChannelEndpointIface,\n type NetworkSandboxSessionIface,\n type SandboxProviderHandleIface,\n type SandboxProviderIface,\n type SandboxProviderImplementationIface,\n type SandboxStartContextIface,\n} from '@finalai/agent-provider/sandbox';\nimport * as z from 'zod';\nimport { commandEncoderFor } from \"./commands.mjs\";\nimport {\n createDeviceApiClient,\n DEVICE_API_COMPUTER_USE_SERVER,\n DeviceApiCapability,\n DeviceApiLeaseAccess,\n type DeviceApiCallerIface,\n type DeviceApiConfigIface,\n type DeviceApiDeviceIface,\n type DeviceApiFetchIface,\n type DeviceApiLeaseIface,\n} from \"./device-api.mjs\";\nimport { computerOpenOptionsSchema, deviceEnvironmentSchema } from \"./environment.mjs\";\nimport { createMcpPipe } from \"./mcp-pipe.mjs\";\nimport { sandboxPathsFor, SandboxPlatform } from \"./paths.mjs\";\nimport { createTransportSession, sessionForAccess } from \"./session.mjs\";\n\nexport { DEVICE_API_ACTING_USER_HEADER, DeviceApiClientErrorCode } from \"./device-api.mjs\";\nexport type { DeviceApiConfigIface, DeviceApiFetchIface } from \"./device-api.mjs\";\nexport { DEVICE_SANDBOX_SPECIFIER } from \"./environment.mjs\";\nexport type { FinalPlatformAccessIface } from '@finalai/agent-provider/sandbox';\nexport { SandboxPlatform } from \"./paths.mjs\";\n\n// The `device` sandbox: a user's own computer running Final Device. The\n// provider is a client of the Device API, the Device's backend, exactly as a\n// vendor provider is a client of its vendor's API: it\n// leases a workspace there and drives files, processes, computer use and the\n// screen through that API with host-provided credentials. It knows nothing of how\n// the Device API reaches the Device (DEVICE_DIRECT_ENDPOINT.md). A Device is one\n// person's machine: every call names the user the session acts for\n// (ctx.actingUser, read per call since it changes between turns), and the Device\n// API refuses a Device that user does not own.\n\nexport interface DeviceSandboxProviderConfigIface {\n // Transport options; the Device API origin and token come from each context's\n // `credentials.deviceAccess`.\n api: DeviceApiConfigIface;\n fetch?: DeviceApiFetchIface;\n // Device leases last at most an hour; renewed while in use.\n leaseTtlMs?: number;\n // How long start and resume wait for an offline Device to connect (default 0:\n // an offline Device fails at once); polled from 500 ms, doubling to 5 s.\n onlineWaitMs?: number;\n sleep?: (ms: number) => Promise<void>;\n log?: (message: string, fields?: Record<string, string | number | boolean | null>) => void;\n randomUuid?: () => string;\n now?: () => number;\n}\n\nconst deviceArtifactSchema = z.object({ version: z.literal(1), deviceId: z.string() });\nconst deviceStateSchema = z.object({\n version: z.literal(1),\n deviceId: z.string(),\n generation: z.number().int().min(1),\n access: z.nativeEnum(SandboxAccess),\n});\nexport type DeviceSandboxArtifact = z.infer<typeof deviceArtifactSchema>;\nexport type DeviceSandboxState = z.infer<typeof deviceStateSchema>;\n\nexport const DEVICE_COMPUTER_USE_SERVER = DEVICE_API_COMPUTER_USE_SERVER;\n\nexport interface DeviceSandboxSessionIface extends NetworkSandboxSessionIface {\n readonly capabilities: readonly SandboxCapability[];\n}\n\nexport type DeviceSandboxProviderIface = SandboxProviderIface<\n JsonValue,\n JsonValue,\n DeviceSandboxArtifact,\n DeviceSandboxState,\n DeviceSandboxSessionIface\n>;\n\n// The Device API's recorded tools as tool descriptors; one that does not fit\n// the contract is left out.\nconst descriptorsOf = (tools: readonly JsonObject[]): ToolDescriptorIface[] =>\n tools.flatMap((tool) => {\n const parsed = toolDescriptorSchema.safeParse({\n ...tool,\n description: tool.description ?? tool.title ?? tool.name,\n });\n return parsed.success ? [parsed.data] : [];\n });\n\nconst LEASE_RENEW_MARGIN_MS = 5 * 60_000;\nconst ONLINE_POLL_MS = 500;\nconst ONLINE_POLL_MAX_MS = 5_000;\nconst MAX_LEASE_TTL_MS = 60 * 60_000;\nconst MIN_LEASE_TTL_MS = 60_000;\n\nconst platformOf = (platform: string): SandboxPlatform =>\n platform === 'windows' ? SandboxPlatform.Windows : SandboxPlatform.Posix;\n\nexport const createDeviceSandboxProvider = (\n config: DeviceSandboxProviderConfigIface,\n): DeviceSandboxProviderIface => {\n const randomUuid = config.randomUuid ?? (() => crypto.randomUUID());\n const now = config.now ?? (() => Date.now());\n const log = config.log ?? (() => undefined);\n const leaseTtlMs = Math.max(\n MIN_LEASE_TTL_MS,\n Math.min(config.leaseTtlMs ?? 15 * 60_000, MAX_LEASE_TTL_MS),\n );\n const sleep =\n config.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n const onlineWaitMs = config.onlineWaitMs ?? 0;\n const api = createDeviceApiClient({ api: config.api, fetch: config.fetch });\n\n // A Device runs when it is connected to the Device API; nothing is leased before.\n const awaitOnline = async (\n caller: DeviceApiCallerIface,\n deviceId: string,\n timeoutMs: number,\n ): Promise<DeviceApiDeviceIface> => {\n const deadline = now() + timeoutMs;\n let pollMs = ONLINE_POLL_MS;\n for (;;) {\n const device = await api.getDevice({ caller, deviceId });\n if (device == null) throw new Error(`device ${deviceId} was not found`);\n if (device.online) return device;\n const left = deadline - now();\n if (left <= 0) throw new Error(`${device.displayName} is not connected`);\n await sleep(Math.min(pollMs, left));\n pollMs = Math.min(pollMs * 2, ONLINE_POLL_MAX_MS);\n }\n };\n\n const attach = async (args: {\n ctx: SandboxStartContextIface;\n deviceId: string;\n afterGeneration: number | null;\n passive?: boolean;\n access: SandboxAccess;\n }): Promise<{\n handle: SandboxProviderHandleIface<DeviceSandboxSessionIface>;\n generation: number;\n }> => {\n const { ctx, access } = args;\n const caller: DeviceApiCallerIface = {\n organizationId: ctx.session.organizationId,\n credentials: ctx.credentials,\n actingUser: ctx.actingUser,\n };\n const bindingId = ctx.session.bindingId;\n // The provider's awaitRunning, before the lease.\n const device = await awaitOnline(caller, args.deviceId, onlineWaitMs);\n // Processes need the Device's write lease, so execute access does too.\n if (access !== SandboxAccess.Read && !device.writable) {\n throw new Error(`${device.displayName} does not permit writes`);\n }\n const leaseAccess =\n access === SandboxAccess.Read ? DeviceApiLeaseAccess.Read : DeviceApiLeaseAccess.Write;\n let current: DeviceApiLeaseIface =\n args.passive === true\n ? await api.recoverLease({\n caller,\n deviceId: device.id,\n sessionId: ctx.session.id,\n bindingId,\n access: leaseAccess,\n })\n : await api.lease({\n caller,\n deviceId: device.id,\n sessionId: ctx.session.id,\n bindingId,\n access: leaseAccess,\n ttlMs: leaseTtlMs,\n afterGeneration: args.afterGeneration ?? undefined,\n });\n if (\n args.passive === true &&\n (current.deviceId !== device.id ||\n current.access !== leaseAccess ||\n current.generation < (args.afterGeneration ?? 0) ||\n current.expiresAtMs <= now())\n )\n throw new Error('The existing Device lease is unavailable');\n let renewing: Promise<DeviceApiLeaseIface> | null = null;\n // The current lease handle, renewed shortly before it lapses.\n const lease = async (): Promise<string> => {\n if (current.expiresAtMs - now() < LEASE_RENEW_MARGIN_MS) {\n renewing ??= (\n args.passive === true\n ? api.recoverLease({\n caller,\n deviceId: device.id,\n sessionId: ctx.session.id,\n bindingId,\n access: leaseAccess,\n })\n : api.renew({ caller, lease: current.lease, ttlMs: leaseTtlMs })\n ).finally(() => {\n renewing = null;\n });\n current = await renewing;\n }\n return current.lease;\n };\n let released = false;\n const release = async (): Promise<void> => {\n if (released) return;\n released = true;\n await api.release({ caller, lease: current.lease });\n };\n\n const platform = platformOf(current.platform);\n const paths = sandboxPathsFor(platform);\n const session = createTransportSession({\n id: `${device.id}:${bindingId}`,\n description: `${device.displayName} (Final Device)`,\n workspaceRoot: current.workspaceRoot,\n transport: api.transport({ caller, lease }),\n baseEnv: async () => ({}),\n randomUuid,\n paths,\n commands: commandEncoderFor(platform),\n // Lease processes end with the lease generation; a new one knows none.\n processScope: () => String(current.generation),\n network: {\n ports: [],\n getPortEndpoint: async () => {\n throw new Error('Final Device sandboxes expose no ports');\n },\n stop: release,\n destroy: release,\n },\n });\n\n const offered = new Set(current.capabilities);\n const processes = offered.has(DeviceApiCapability.Process);\n const localMcp = offered.has(DeviceApiCapability.LocalMcp);\n const computerUse = offered.has(DeviceApiCapability.ComputerUse);\n const capabilities = [SandboxCapability.Files];\n if (processes) capabilities.push(SandboxCapability.Process);\n if (processes && access !== SandboxAccess.Read)\n capabilities.push(SandboxCapability.DurableProcesses);\n if (localMcp) capabilities.push(SandboxCapability.LocalMcp);\n if (computerUse) capabilities.push(SandboxCapability.ComputerUse);\n if (device.screenReady) capabilities.push(SandboxCapability.Screen);\n\n // The Device's computer-use server, or one of its local MCP servers, is a byte\n // pipe in the Device API (`/v1/leases/:lease/mcp/:server/{open,write,read,close}`):\n // a channel endpoint whoever runs this handle opens in process.\n const channelEndpoint = (server: string): McpChannelEndpointIface => ({\n name: server,\n connect: async () => {\n const pipe = openMcpPipe(server);\n return {\n send: (message, options) => pipe.send(message, options?.abortSignal),\n receive: (options) => pipe.receive(options?.abortSignal),\n close: () => pipe.close(),\n };\n },\n });\n const openMcpPipe = (server: string) => {\n const clientId = randomUuid();\n const request = async (\n action: 'open' | 'write' | 'read' | 'close',\n dataBase64?: string,\n signal?: AbortSignal,\n ): Promise<string> =>\n api.pipe({\n caller,\n lease: await lease(),\n server,\n action,\n clientId,\n dataBase64,\n signal,\n });\n return createMcpPipe({\n wire: {\n open: async (signal) => {\n await request('open', undefined, signal);\n },\n write: async (data, signal) => {\n await request('write', data, signal);\n },\n read: (signal) => request('read', undefined, signal),\n close: async () => {\n await request('close').catch(() => undefined);\n },\n },\n });\n };\n const sandbox: DeviceSandboxSessionIface = { ...session, capabilities };\n // Without process access there is nothing to attach to.\n if (!processes) delete sandbox.attachProcess;\n if (computerUse) {\n sandbox.computerUse = async () => channelEndpoint(DEVICE_COMPUTER_USE_SERVER);\n }\n if (localMcp) {\n sandbox.localMcp = async () =>\n (await api.localMcpServers({ caller, lease: await lease() })).map(channelEndpoint);\n }\n if (device.screenReady) {\n sandbox.screen = (options) =>\n api.screen({\n caller,\n deviceId: device.id,\n sessionId: randomUuid(),\n viewOnly: options?.viewOnly ?? true,\n });\n }\n log('device_sandbox.attached', { deviceId: device.id, generation: current.generation });\n return {\n generation: current.generation,\n handle: {\n // The Device enforces the read lease too; the view refuses before a round trip.\n sandbox: sessionForAccess(sandbox, access),\n onSandboxStop: release,\n onSandboxDelete: release,\n onRuntimeShutdown: async () => undefined,\n },\n };\n };\n\n const environment = (\n rawOptions?: JsonValue,\n ): SandboxProviderImplementationIface<\n JsonValue,\n DeviceSandboxArtifact,\n DeviceSandboxState,\n DeviceSandboxSessionIface\n > => {\n const parsed = deviceEnvironmentSchema.safeParse(rawOptions ?? {});\n if (!parsed.success) throw new Error(`invalid device environment: ${parsed.error.message}`);\n const options = parsed.data;\n // The Device the environment names; the Device API checks on every call\n // that the acting user owns it.\n const resolveDeviceId = async (caller: DeviceApiCallerIface): Promise<string> => {\n if ('deviceId' in options) return options.deviceId;\n if ('pairing' in options) {\n const device = await api.resolvePairing({ caller, code: options.pairing });\n if (device == null) throw new Error('the Final Device pairing code is unknown or expired');\n return device.id;\n }\n // An unnamed binding uses the only Device of the user the session acts\n // for (the API lists only that user's Devices), never another member's.\n const devices = await api.listDevices({ caller });\n const [only] = devices;\n if (devices.length !== 1 || only == null) {\n throw new Error(\n devices.length === 0\n ? 'the user this session acts for has no Final Device'\n : 'the user this session acts for has several Final Devices; name one by id or pairing code',\n );\n }\n return only.id;\n };\n return {\n processes: async (ctx, rawState, request) => {\n const state = deviceStateSchema.parse(rawState);\n if (\n request.operation === ProcessInspectionOperation.Stop &&\n state.access === SandboxAccess.Read\n )\n throw new Error('stopping processes requires execute access');\n return api.inspectProcesses({\n caller: ctx,\n deviceId: state.deviceId,\n bindingId: ctx.bindingId,\n request,\n });\n },\n // What a lease on this Device grants, known from the registry without\n // leasing (the Device API's rule: processes, local MCP and computer use with\n // a write lease on a Device that allows processes). The computer-use\n // driver's readiness is checked when leased.\n capabilities: async (ctx) => {\n const device = await api.getDevice({\n caller: ctx,\n deviceId: await resolveDeviceId(ctx),\n });\n if (device == null) return null;\n const offered = [SandboxCapability.Files];\n if (device.writable && device.allowProcesses) {\n offered.push(\n SandboxCapability.Process,\n SandboxCapability.DurableProcesses,\n SandboxCapability.LocalMcp,\n SandboxCapability.ComputerUse,\n );\n }\n if (device.screenReady) offered.push(SandboxCapability.Screen);\n return offered;\n },\n // What the Device's servers last listed through the Device API (it records\n // them from the MCP pipes), so the projection lists them without leasing.\n // A Device the API has never seen list gives unknown; its first lease does.\n mcpCatalog: async (ctx) => {\n const catalog = await api.mcpCatalog({\n caller: ctx,\n deviceId: await resolveDeviceId(ctx),\n });\n const local = catalog.localServers;\n const localMcp: Record<string, ToolDescriptorIface[]> = {};\n let localKnown = local != null;\n for (const server of local?.servers ?? []) {\n if (server.tools == null) localKnown = false;\n else localMcp[server.name] = descriptorsOf(server.tools);\n }\n return {\n computerUse:\n catalog.computerUse == null ? null : descriptorsOf(catalog.computerUse.tools),\n localMcp: localKnown ? localMcp : null,\n };\n },\n awaitRunning: async (ctx, rawState, { timeoutMs }) => {\n const { deviceId } = deviceStateSchema.pick({ deviceId: true }).parse(rawState);\n await awaitOnline(\n {\n organizationId: ctx.session.organizationId,\n credentials: ctx.credentials,\n actingUser: ctx.actingUser,\n },\n deviceId,\n timeoutMs,\n );\n },\n artifactKey: async (ctx) => `device:${await resolveDeviceId(ctx)}`,\n prepare: async (ctx) => ({ version: 1, deviceId: await resolveDeviceId(ctx) }),\n start: async (ctx, openOptions, rawArtifact) => {\n const artifact = deviceArtifactSchema.parse(rawArtifact);\n const access = computerOpenOptionsSchema.parse(openOptions ?? {}).access;\n const { handle, generation } = await attach({\n ctx,\n deviceId: artifact.deviceId,\n afterGeneration: null,\n access,\n });\n return { handle, state: { version: 1, deviceId: artifact.deviceId, generation, access } };\n },\n // A Device never takes a lease back at an old generation once it lapsed,\n // so every attach leases a later one.\n resume: async (ctx, _artifact, rawState) => {\n const state = deviceStateSchema.parse(rawState);\n return (\n await attach({\n ctx,\n deviceId: state.deviceId,\n afterGeneration: state.generation,\n access: state.access,\n })\n ).handle;\n },\n resumePassive: async (ctx, _artifact, rawState) => {\n const state = deviceStateSchema.parse(rawState);\n return (\n await attach({\n ctx,\n deviceId: state.deviceId,\n afterGeneration: state.generation,\n access: state.access,\n passive: true,\n })\n ).handle;\n },\n onSessionEnd: async (ctx, _artifact, rawState) => {\n const state = deviceStateSchema.parse(rawState);\n await api.releaseWorkspaceLeases({\n caller: {\n organizationId: ctx.session.organizationId,\n credentials: ctx.credentials,\n actingUser: ctx.actingUser,\n },\n deviceId: state.deviceId,\n bindingId: ctx.session.bindingId,\n });\n },\n // The lease lapses on its own; the next generation retires its processes.\n park: async (_ctx, _artifact, rawState) => deviceStateSchema.parse(rawState),\n };\n };\n\n return defineSandboxProvider({\n name: 'device',\n stateProtocolVersion: 1,\n environment,\n });\n};\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,
|
|
6
|
-
"names": ["SandboxPlatform"]
|
|
4
|
+
"sourcesContent": ["import { Capability, FilesAccess, ScreenAccess, ShellAccess } from '@finalai/agent-protocol';\nimport {\n toolDescriptorSchema,\n type JsonObject,\n type JsonValue,\n type ToolDescriptorIface,\n} from '@finalai/agent-provider';\nimport {\n defineSandboxProvider,\n SandboxCapability,\n type McpChannelEndpointIface,\n type NetworkSandboxSessionIface,\n type SandboxCapabilityOffer,\n type SandboxEnvironmentImplementationIface,\n type SandboxProviderContextIface,\n type SandboxProviderHandleIface,\n type SandboxProviderIface,\n type SandboxResourceContextIface,\n type SandboxSetupIface,\n} from '@finalai/agent-provider/sandbox';\nimport * as z from 'zod';\nimport type { DeviceApiCredentialsIface } from \"./access.mjs\";\nimport { commandEncoderFor } from \"./commands.mjs\";\nimport {\n createDeviceApiClient,\n DEVICE_API_COMPUTER_USE_SERVER,\n DeviceApiCapability,\n DeviceApiLeaseAccess,\n type DeviceApiCallerIface,\n type DeviceApiConfigIface,\n type DeviceApiDeviceIface,\n type DeviceApiFetchIface,\n type DeviceApiLeaseIface,\n} from \"./device-api.mjs\";\nimport { DEVICE_PROVIDER, deviceEnvironmentSchema } from \"./environment.mjs\";\nimport { createMcpPipe } from \"./mcp-pipe.mjs\";\nimport { sandboxPathsFor, SandboxPlatform } from \"./paths.mjs\";\nimport { createTransportSession, readOnlySession } from \"./session.mjs\";\n\nexport { DEVICE_API_ACTING_USER_HEADER, DeviceApiClientErrorCode } from \"./device-api.mjs\";\nexport type { DeviceApiConfigIface, DeviceApiFetchIface } from \"./device-api.mjs\";\nexport { DEVICE_PROVIDER } from \"./environment.mjs\";\nexport type { FinalPlatformAccessIface } from \"./access.mjs\";\nexport { SandboxPlatform } from \"./paths.mjs\";\n\n// The `device` sandbox: a person's own computer running Final Device. The\n// provider is a client of the Device API, the Device's backend, exactly as a\n// vendor provider is a client of its vendor's API: it leases a workspace there\n// and drives files, processes, computer use and the screen through that API.\n// It knows nothing of how the Device API reaches the Device\n// (DEVICE_DIRECT_ENDPOINT.md). A Device is one person's machine: every call\n// names the user the sandbox acts for (ctx.actingUser, read per call since it\n// changes between turns), and the Device API refuses a Device that user does\n// not own. A Device takes no sandbox setup: it is the person's machine as it is.\n\nexport interface DeviceSandboxProviderConfigIface {\n // Transport options.\n api: DeviceApiConfigIface;\n // The Device API origin. Requests authenticate with the provider's platform\n // identity for it (ctx.identity), which names the acting user and which the\n // registration must be granted.\n deviceApiUrl: string;\n fetch?: DeviceApiFetchIface;\n // Device leases last at most an hour; renewed while in use.\n leaseTtlMs?: number;\n // How long attach waits for an offline Device to connect (default 0:\n // an offline Device fails at once); polled from 500 ms, doubling to 5 s.\n onlineWaitMs?: number;\n sleep?: (ms: number) => Promise<void>;\n log?: (message: string, fields?: Record<string, string | number | boolean | null>) => void;\n randomUuid?: () => string;\n now?: () => number;\n}\n\nconst deviceArtifactSchema = z.object({ version: z.literal(1), deviceId: z.string() });\n// `generation`: the last lease generation taken; 0 before the first attach.\nconst deviceStateSchema = z.object({\n version: z.literal(2),\n deviceId: z.string(),\n generation: z.number().int().min(0),\n});\nexport type DeviceSandboxArtifact = z.infer<typeof deviceArtifactSchema>;\nexport type DeviceSandboxState = z.infer<typeof deviceStateSchema>;\n\nexport const DEVICE_COMPUTER_USE_SERVER = DEVICE_API_COMPUTER_USE_SERVER;\n\nexport interface DeviceSandboxSessionIface extends NetworkSandboxSessionIface {\n readonly capabilities: readonly SandboxCapability[];\n}\n\nexport type DeviceSandboxProviderIface = SandboxProviderIface<\n JsonValue,\n DeviceSandboxArtifact,\n DeviceSandboxState,\n DeviceSandboxSessionIface\n>;\n\n// The Device API's recorded tools as tool descriptors; one that does not fit\n// the contract is left out.\nconst descriptorsOf = (tools: readonly JsonObject[]): ToolDescriptorIface[] =>\n tools.flatMap((tool) => {\n const parsed = toolDescriptorSchema.safeParse({\n ...tool,\n description: tool.description ?? tool.title ?? tool.name,\n });\n return parsed.success ? [parsed.data] : [];\n });\n\nconst LEASE_RENEW_MARGIN_MS = 5 * 60_000;\nconst ONLINE_POLL_MS = 500;\nconst ONLINE_POLL_MAX_MS = 5_000;\nconst MAX_LEASE_TTL_MS = 60 * 60_000;\nconst MIN_LEASE_TTL_MS = 60_000;\n\nconst platformOf = (platform: string): SandboxPlatform =>\n platform === 'windows' ? SandboxPlatform.Windows : SandboxPlatform.Posix;\n\n// A Device is the person's machine as it is: nothing is cloned, installed or\n// started on it, and its network is the person's. Notes may still describe it.\nconst refuseSetup = (setup: SandboxSetupIface | null): void => {\n if (setup == null) return;\n const parts = [\n setup.repositories.length > 0 ? 'repositories' : null,\n Object.keys(setup.variables).length > 0 ? 'variables' : null,\n setup.steps.length > 0 ? 'steps' : null,\n setup.services.length > 0 ? 'services' : null,\n Object.keys(setup.commands).length > 0 ? 'commands' : null,\n setup.network != null ? 'a network policy' : null,\n ].filter((part) => part != null);\n if (parts.length > 0)\n throw new Error(`a Final Device takes no sandbox setup (this one has ${parts.join(', ')})`);\n};\n\nexport const createDeviceSandboxProvider = (\n config: DeviceSandboxProviderConfigIface,\n): DeviceSandboxProviderIface => {\n const randomUuid = config.randomUuid ?? (() => crypto.randomUUID());\n const now = config.now ?? (() => Date.now());\n const log = config.log ?? (() => undefined);\n const leaseTtlMs = Math.max(\n MIN_LEASE_TTL_MS,\n Math.min(config.leaseTtlMs ?? 15 * 60_000, MAX_LEASE_TTL_MS),\n );\n const sleep =\n config.sleep ?? ((ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms)));\n const onlineWaitMs = config.onlineWaitMs ?? 0;\n const api = createDeviceApiClient({ api: config.api, fetch: config.fetch });\n\n // A Device runs when it is connected to the Device API; nothing is leased before.\n const awaitOnline = async (\n caller: DeviceApiCallerIface,\n deviceId: string,\n timeoutMs: number,\n ): Promise<DeviceApiDeviceIface> => {\n const deadline = now() + timeoutMs;\n let pollMs = ONLINE_POLL_MS;\n for (;;) {\n const device = await api.getDevice({ caller, deviceId });\n if (device == null) throw new Error(`device ${deviceId} was not found`);\n if (device.online) return device;\n const left = deadline - now();\n if (left <= 0) throw new Error(`${device.displayName} is not connected`);\n await sleep(Math.min(pollMs, left));\n pollMs = Math.min(pollMs * 2, ONLINE_POLL_MAX_MS);\n }\n };\n\n // An assertion for the Device API before each request, and a new one after\n // a rejection; it carries the acting user it was issued for.\n const credentialsFor = (ctx: SandboxProviderContextIface): DeviceApiCredentialsIface => ({\n deviceAccess: async (args) => ({\n baseUrl: config.deviceApiUrl,\n token: (\n await ctx.identity({ audience: config.deviceApiUrl, refresh: args?.refresh === true })\n ).token,\n }),\n });\n\n const callerFor = (ctx: SandboxProviderContextIface): DeviceApiCallerIface => ({\n organizationId: ctx.organizationId,\n credentials: credentialsFor(ctx),\n actingUser: ctx.actingUser,\n });\n\n const lease = async (args: {\n ctx: SandboxResourceContextIface;\n deviceId: string;\n afterGeneration: number;\n passive: boolean;\n }): Promise<{\n handle: SandboxProviderHandleIface<DeviceSandboxSessionIface>;\n generation: number;\n }> => {\n const { ctx } = args;\n const caller = callerFor(ctx);\n // A Device workspace is keyed by the sandbox: every attach of this sandbox\n // finds the same files, and its processes end with each lease generation.\n const bindingId = ctx.sandboxId;\n // The provider's awaitRunning, before the lease.\n const device = await awaitOnline(caller, args.deviceId, onlineWaitMs);\n // The host gates each call by its grant; the lease is the most the Device allows.\n const leaseAccess = device.writable ? DeviceApiLeaseAccess.Write : DeviceApiLeaseAccess.Read;\n const afterGeneration = args.afterGeneration === 0 ? undefined : args.afterGeneration;\n let current: DeviceApiLeaseIface = args.passive\n ? await api.recoverLease({\n caller,\n deviceId: device.id,\n sessionId: ctx.sandboxId,\n bindingId,\n access: leaseAccess,\n })\n : await api.lease({\n caller,\n deviceId: device.id,\n sessionId: ctx.sandboxId,\n bindingId,\n access: leaseAccess,\n ttlMs: leaseTtlMs,\n afterGeneration,\n });\n if (\n args.passive &&\n (current.deviceId !== device.id ||\n current.generation < args.afterGeneration ||\n current.expiresAtMs <= now())\n )\n throw new Error('The existing Device lease is unavailable');\n let renewing: Promise<DeviceApiLeaseIface> | null = null;\n // The current lease handle, renewed shortly before it lapses.\n const leaseHandle = async (): Promise<string> => {\n if (current.expiresAtMs - now() < LEASE_RENEW_MARGIN_MS) {\n renewing ??= (\n args.passive\n ? api.recoverLease({\n caller,\n deviceId: device.id,\n sessionId: ctx.sandboxId,\n bindingId,\n access: leaseAccess,\n })\n : api.renew({ caller, lease: current.lease, ttlMs: leaseTtlMs })\n ).finally(() => {\n renewing = null;\n });\n current = await renewing;\n }\n return current.lease;\n };\n let released = false;\n const release = async (): Promise<void> => {\n if (released || args.passive) return;\n released = true;\n await api.release({ caller, lease: current.lease });\n };\n\n const platform = platformOf(current.platform);\n const paths = sandboxPathsFor(platform);\n const session = createTransportSession({\n id: `${device.id}:${bindingId}`,\n description: `${device.displayName} (Final Device)`,\n workspaceRoot: current.workspaceRoot,\n transport: api.transport({ caller, lease: leaseHandle }),\n baseEnv: async () => ({}),\n randomUuid,\n paths,\n commands: commandEncoderFor(platform),\n // Lease processes end with the lease generation; a new one knows none.\n processScope: () => String(current.generation),\n network: {\n ports: [],\n getPortEndpoint: async () => {\n throw new Error('Final Device sandboxes expose no ports');\n },\n stop: release,\n destroy: release,\n },\n });\n\n const offered = new Set(current.capabilities);\n const processes = offered.has(DeviceApiCapability.Process);\n const localMcp = offered.has(DeviceApiCapability.LocalMcp);\n const computerUse = offered.has(DeviceApiCapability.ComputerUse);\n const capabilities = [SandboxCapability.Files];\n if (processes) capabilities.push(SandboxCapability.Process);\n if (processes && !args.passive) capabilities.push(SandboxCapability.DurableProcesses);\n if (localMcp) capabilities.push(SandboxCapability.LocalMcp);\n if (computerUse) capabilities.push(SandboxCapability.ComputerUse);\n if (device.screenReady) capabilities.push(SandboxCapability.Screen);\n\n // The Device's computer-use server, or one of its local MCP servers, is a byte\n // pipe in the Device API (`/v1/leases/:lease/mcp/:server/{open,write,read,close}`):\n // a channel endpoint whoever runs this handle opens in process.\n const openMcpPipe = (server: string) => {\n const clientId = randomUuid();\n const request = async (\n action: 'open' | 'write' | 'read' | 'close',\n dataBase64?: string,\n signal?: AbortSignal,\n ): Promise<string> =>\n api.pipe({\n caller,\n lease: await leaseHandle(),\n server,\n action,\n clientId,\n dataBase64,\n signal,\n });\n return createMcpPipe({\n wire: {\n open: async (signal) => {\n await request('open', undefined, signal);\n },\n write: async (data, signal) => {\n await request('write', data, signal);\n },\n read: (signal) => request('read', undefined, signal),\n close: async () => {\n await request('close').catch(() => undefined);\n },\n },\n });\n };\n const channelEndpoint = (server: string): McpChannelEndpointIface => ({\n name: server,\n connect: async () => {\n const pipe = openMcpPipe(server);\n return {\n send: (message, options) => pipe.send(message, options?.abortSignal),\n receive: (options) => pipe.receive(options?.abortSignal),\n close: () => pipe.close(),\n };\n },\n });\n const sandbox: DeviceSandboxSessionIface = { ...session, capabilities };\n // Without process access there is nothing to attach to.\n if (!processes) delete sandbox.attachProcess;\n if (computerUse) {\n sandbox.computerUse = async () => channelEndpoint(DEVICE_COMPUTER_USE_SERVER);\n }\n if (localMcp) {\n sandbox.localMcp = async () =>\n (await api.localMcpServers({ caller, lease: await leaseHandle() })).map(channelEndpoint);\n }\n if (device.screenReady) {\n sandbox.screen = (options) =>\n api.screen({\n caller,\n deviceId: device.id,\n sessionId: randomUuid(),\n viewOnly: options?.viewOnly ?? true,\n });\n }\n log('device_sandbox.attached', { deviceId: device.id, generation: current.generation });\n return {\n generation: current.generation,\n handle: {\n // A viewer's lease is the existing one: it changes nothing.\n sandbox: args.passive ? readOnlySession(sandbox) : sandbox,\n onSandboxStop: release,\n onSandboxDelete: release,\n onRuntimeShutdown: async () => undefined,\n },\n };\n };\n\n const environment = (\n rawOptions?: JsonValue,\n ): SandboxEnvironmentImplementationIface<\n DeviceSandboxArtifact,\n DeviceSandboxState,\n DeviceSandboxSessionIface\n > => {\n const parsed = deviceEnvironmentSchema.safeParse(rawOptions ?? {});\n if (!parsed.success) throw new Error(`invalid device environment: ${parsed.error.message}`);\n const options = parsed.data;\n // The Device the environment names; the Device API checks on every call\n // that the acting user owns it.\n const resolveDeviceId = async (caller: DeviceApiCallerIface): Promise<string> => {\n if ('deviceId' in options) return options.deviceId;\n const device = await api.resolvePairing({ caller, code: options.pairing });\n if (device == null) throw new Error('the Final Device pairing code is unknown or expired');\n return device.id;\n };\n return {\n // What a lease on this Device grants, known from the registry without\n // leasing (the Device API's rule: processes and computer use with a write\n // lease on a Device that allows processes).\n capabilities: async (ctx): Promise<SandboxCapabilityOffer> => {\n const device = await api.getDevice({\n caller: callerFor(ctx),\n deviceId: await resolveDeviceId(callerFor(ctx)),\n });\n if (device == null) return {};\n const offer: SandboxCapabilityOffer = {\n [Capability.Files]: device.writable ? FilesAccess.Write : FilesAccess.Read,\n };\n if (device.writable && device.allowProcesses) {\n offer[Capability.Shell] = ShellAccess.Execute;\n offer[Capability.ComputerUse] = ScreenAccess.Control;\n } else if (device.screenReady) offer[Capability.ComputerUse] = ScreenAccess.View;\n return offer;\n },\n features: async (ctx) => {\n const device = await api.getDevice({\n caller: callerFor(ctx),\n deviceId: await resolveDeviceId(callerFor(ctx)),\n });\n if (device == null) return null;\n const offered = [SandboxCapability.Files];\n if (device.writable && device.allowProcesses) {\n offered.push(\n SandboxCapability.Process,\n SandboxCapability.DurableProcesses,\n SandboxCapability.LocalMcp,\n SandboxCapability.ComputerUse,\n );\n }\n if (device.screenReady) offered.push(SandboxCapability.Screen);\n return offered;\n },\n // What the Device's servers last listed through the Device API (it records\n // them from the MCP pipes), so the projection lists them without leasing.\n // A Device the API has never seen list gives unknown; its first lease does.\n mcpCatalog: async (ctx) => {\n const catalog = await api.mcpCatalog({\n caller: callerFor(ctx),\n deviceId: await resolveDeviceId(callerFor(ctx)),\n });\n const local = catalog.localServers;\n const localMcp: Record<string, ToolDescriptorIface[]> = {};\n let localKnown = local != null;\n for (const server of local?.servers ?? []) {\n if (server.tools == null) localKnown = false;\n else localMcp[server.name] = descriptorsOf(server.tools);\n }\n return {\n computerUse:\n catalog.computerUse == null ? null : descriptorsOf(catalog.computerUse.tools),\n localMcp: localKnown ? localMcp : null,\n };\n },\n processes: async (ctx, rawState, request) => {\n const state = deviceStateSchema.parse(rawState);\n return api.inspectProcesses({\n caller: callerFor(ctx),\n deviceId: state.deviceId,\n bindingId: ctx.sandboxId,\n request,\n });\n },\n artifactKey: async (ctx) => `device:${await resolveDeviceId(callerFor(ctx))}`,\n prepare: async (ctx) => ({ version: 1, deviceId: await resolveDeviceId(callerFor(ctx)) }),\n create: async (_ctx, { artifact, setup }) => {\n refuseSetup(setup);\n return { state: { version: 2, deviceId: artifact.deviceId, generation: 0 } };\n },\n awaitRunning: async (ctx, rawState, { timeoutMs }) => {\n const { deviceId } = deviceStateSchema.pick({ deviceId: true }).parse(rawState);\n await awaitOnline(callerFor(ctx), deviceId, timeoutMs);\n },\n // A Device never takes a lease back at an old generation once it lapsed,\n // so every attach leases a later one, and no process from before survives.\n attach: async (ctx, { state: rawState }) => {\n const state = deviceStateSchema.parse(rawState);\n const { handle, generation } = await lease({\n ctx,\n deviceId: state.deviceId,\n afterGeneration: state.generation,\n passive: false,\n });\n return {\n handle,\n state: { ...state, generation },\n fresh: state.generation === 0,\n booted: true,\n };\n },\n attachPassive: async (ctx, { state: rawState }) => {\n const state = deviceStateSchema.parse(rawState);\n return (\n await lease({\n ctx,\n deviceId: state.deviceId,\n afterGeneration: state.generation,\n passive: true,\n })\n ).handle;\n },\n // The lease lapses on its own; the next generation retires its processes.\n detach: async (_ctx, { state }) => deviceStateSchema.parse(state),\n park: async (_ctx, { state }) => deviceStateSchema.parse(state),\n // The Device stays the person's; its workspace for this sandbox is let go.\n delete: async (ctx, { state: rawState }) => {\n const state = deviceStateSchema.parse(rawState);\n await api.releaseWorkspaceLeases({\n caller: callerFor(ctx),\n deviceId: state.deviceId,\n bindingId: ctx.sandboxId,\n });\n },\n };\n };\n\n return defineSandboxProvider({\n name: DEVICE_PROVIDER,\n stateProtocolVersion: 2,\n environment,\n });\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["DEVICE_PROVIDER", "SandboxPlatform"]
|
|
7
7
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { type WorkspaceEgressRequestIface, type WorkspaceEgressStatusIface } from '@finalai/agent-protocol/device-egress';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
2
|
+
import { type SandboxGrantIface, type SandboxNetworkingExtensionIface } from '@finalai/agent-provider/sandbox';
|
|
3
|
+
import type { NetworkSandboxSessionIface, SandboxActingUserIface, SandboxRunResultIface, SandboxProviderHandleIface, NetworkPolicyIface } from '@finalai/agent-provider/sandbox';
|
|
4
|
+
import { type DeviceAccessIface, type DeviceApiCredentialsIface } from "./access.mjs";
|
|
5
5
|
import { type DeviceApiClientIface, type DeviceApiConfigIface } from "./device-api.mjs";
|
|
6
6
|
export interface WorkspaceEgressManagerOptionsIface {
|
|
7
7
|
sandbox: NetworkSandboxSessionIface;
|
|
8
8
|
workspaceId: string;
|
|
9
9
|
organizationId: string;
|
|
10
|
-
credentials?:
|
|
10
|
+
credentials?: DeviceApiCredentialsIface;
|
|
11
11
|
actingUser: SandboxActingUserIface;
|
|
12
12
|
api: DeviceApiConfigIface | (() => DeviceApiConfigIface);
|
|
13
13
|
privilegedRun?: (request: {
|
|
@@ -37,7 +37,7 @@ export interface WorkspaceEgressManagerOptionsIface {
|
|
|
37
37
|
export declare const suspendWorkspaceEgress: (args: {
|
|
38
38
|
api: DeviceApiConfigIface | (() => DeviceApiConfigIface);
|
|
39
39
|
organizationId: string;
|
|
40
|
-
credentials?:
|
|
40
|
+
credentials?: DeviceApiCredentialsIface;
|
|
41
41
|
workspaceId: string;
|
|
42
42
|
actingUser: SandboxActingUserIface;
|
|
43
43
|
}) => Promise<void>;
|
|
@@ -54,7 +54,7 @@ export declare const createWorkspaceEgressManager: (options: WorkspaceEgressMana
|
|
|
54
54
|
export declare const preserveWorkspaceNetworkPolicy: (options: {
|
|
55
55
|
api: DeviceApiConfigIface | (() => DeviceApiConfigIface);
|
|
56
56
|
organizationId: string;
|
|
57
|
-
credentials?:
|
|
57
|
+
credentials?: DeviceApiCredentialsIface;
|
|
58
58
|
workspaceId: string;
|
|
59
59
|
policy: NetworkPolicyIface;
|
|
60
60
|
client?: Pick<DeviceApiClientIface, "egress">;
|
|
@@ -62,12 +62,13 @@ export declare const preserveWorkspaceNetworkPolicy: (options: {
|
|
|
62
62
|
export type WorkspaceEgressManagerIface = ReturnType<typeof createWorkspaceEgressManager>;
|
|
63
63
|
export interface WorkspaceDeviceNetworkingOptionsIface {
|
|
64
64
|
api: WorkspaceEgressManagerOptionsIface['api'];
|
|
65
|
+
access?: DeviceAccessIface;
|
|
65
66
|
mcpFetch?: typeof globalThis.fetch;
|
|
66
67
|
needed: (args: {
|
|
67
68
|
organizationId: string;
|
|
69
|
+
projectId: string;
|
|
68
70
|
workspaceId: string;
|
|
69
71
|
teardown?: boolean;
|
|
70
|
-
access?: SandboxAccess;
|
|
71
72
|
}) => Promise<boolean>;
|
|
72
73
|
allowConfiguration?: boolean;
|
|
73
74
|
authorizeOwnerChange?: (args: {
|
|
@@ -80,14 +81,14 @@ export declare const createWorkspaceDeviceNetworking: (options: WorkspaceDeviceN
|
|
|
80
81
|
export declare const withWorkspaceEgress: <S extends NetworkSandboxSessionIface>(options: {
|
|
81
82
|
handle: SandboxProviderHandleIface<S>;
|
|
82
83
|
manager: WorkspaceEgressManagerIface;
|
|
83
|
-
|
|
84
|
+
grant: SandboxGrantIface;
|
|
84
85
|
beforeUse?: () => Promise<void>;
|
|
85
86
|
initialized?: boolean;
|
|
86
87
|
fetch?: typeof globalThis.fetch;
|
|
87
88
|
}) => Promise<SandboxProviderHandleIface<S>>;
|
|
88
89
|
export declare const withWorkspaceAdmission: <S extends NetworkSandboxSessionIface>(options: {
|
|
89
90
|
handle: SandboxProviderHandleIface<S>;
|
|
90
|
-
|
|
91
|
+
grant: SandboxGrantIface;
|
|
91
92
|
admission: () => Promise<void>;
|
|
92
93
|
fetch?: typeof globalThis.fetch;
|
|
93
94
|
}) => Promise<SandboxProviderHandleIface<S>>;
|