langsmith 0.10.3 → 0.10.5
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/client.cjs +19 -4
- package/dist/client.d.ts +2 -1
- package/dist/client.js +19 -4
- package/dist/evaluation/_runner.cjs +47 -5
- package/dist/evaluation/_runner.d.ts +12 -0
- package/dist/evaluation/_runner.js +47 -5
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/sandbox/access_delegation.cjs +24 -0
- package/dist/sandbox/access_delegation.d.ts +15 -0
- package/dist/sandbox/access_delegation.js +21 -0
- package/dist/sandbox/client.cjs +70 -5
- package/dist/sandbox/client.d.ts +18 -0
- package/dist/sandbox/client.js +70 -5
- package/dist/sandbox/command_handle.cjs +34 -0
- package/dist/sandbox/command_handle.d.ts +10 -0
- package/dist/sandbox/command_handle.js +34 -0
- package/dist/sandbox/helpers.cjs +129 -0
- package/dist/sandbox/helpers.d.ts +29 -0
- package/dist/sandbox/helpers.js +123 -0
- package/dist/sandbox/index.cjs +7 -1
- package/dist/sandbox/index.d.ts +5 -1
- package/dist/sandbox/index.js +2 -0
- package/dist/sandbox/mounts.cjs +21 -3
- package/dist/sandbox/mounts.d.ts +7 -1
- package/dist/sandbox/mounts.js +21 -3
- package/dist/sandbox/proxy_config.cjs +41 -10
- package/dist/sandbox/proxy_config.d.ts +33 -6
- package/dist/sandbox/proxy_config.js +40 -10
- package/dist/sandbox/sandbox.cjs +160 -4
- package/dist/sandbox/sandbox.d.ts +71 -1
- package/dist/sandbox/sandbox.js +161 -5
- package/dist/sandbox/service_url.cjs +125 -0
- package/dist/sandbox/service_url.d.ts +50 -0
- package/dist/sandbox/service_url.js +120 -0
- package/dist/sandbox/types.d.ts +251 -8
- package/dist/sandbox/ws_execute.cjs +31 -1
- package/dist/sandbox/ws_execute.d.ts +9 -0
- package/dist/sandbox/ws_execute.js +31 -1
- package/dist/utils/error.cjs +17 -22
- package/dist/utils/error.js +17 -22
- package/dist/wrappers/openai_agents.cjs +37 -28
- package/dist/wrappers/openai_agents.d.ts +0 -1
- package/dist/wrappers/openai_agents.js +37 -28
- package/package.json +2 -2
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ServiceLoginUrl = exports.ServiceUrl = exports.SERVICE_TOKEN_HEADER = void 0;
|
|
4
|
+
/** Header the sandbox router reads the minted service token from. */
|
|
5
|
+
exports.SERVICE_TOKEN_HEADER = "X-Langsmith-Sandbox-Service-Token";
|
|
6
|
+
/** Refresh this many seconds before the token actually expires. */
|
|
7
|
+
const REFRESH_MARGIN_SECONDS = 60;
|
|
8
|
+
/**
|
|
9
|
+
* Service URL carrying a short-lived token, refreshed as it nears expiry.
|
|
10
|
+
*
|
|
11
|
+
* Accessors are async because a refresh is a network call.
|
|
12
|
+
*/
|
|
13
|
+
class ServiceUrl {
|
|
14
|
+
constructor(data, refresher) {
|
|
15
|
+
Object.defineProperty(this, "_browserUrl", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true,
|
|
19
|
+
value: void 0
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(this, "_serviceUrl", {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: true,
|
|
24
|
+
writable: true,
|
|
25
|
+
value: void 0
|
|
26
|
+
});
|
|
27
|
+
Object.defineProperty(this, "_token", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
writable: true,
|
|
31
|
+
value: void 0
|
|
32
|
+
});
|
|
33
|
+
Object.defineProperty(this, "_expiresAt", {
|
|
34
|
+
enumerable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
writable: true,
|
|
37
|
+
value: void 0
|
|
38
|
+
});
|
|
39
|
+
Object.defineProperty(this, "_refresher", {
|
|
40
|
+
enumerable: true,
|
|
41
|
+
configurable: true,
|
|
42
|
+
writable: true,
|
|
43
|
+
value: void 0
|
|
44
|
+
});
|
|
45
|
+
this._browserUrl = data.browser_url ?? "";
|
|
46
|
+
this._serviceUrl = data.service_url ?? "";
|
|
47
|
+
this._token = data.token ?? "";
|
|
48
|
+
this._expiresAt = data.expires_at ?? "";
|
|
49
|
+
this._refresher = refresher;
|
|
50
|
+
}
|
|
51
|
+
_shouldRefresh() {
|
|
52
|
+
if (!this._refresher || !this._expiresAt)
|
|
53
|
+
return false;
|
|
54
|
+
const expires = Date.parse(this._expiresAt);
|
|
55
|
+
if (Number.isNaN(expires))
|
|
56
|
+
return false;
|
|
57
|
+
return (expires - Date.now()) / 1000 <= REFRESH_MARGIN_SECONDS;
|
|
58
|
+
}
|
|
59
|
+
async _maybeRefresh() {
|
|
60
|
+
if (!this._refresher || !this._shouldRefresh())
|
|
61
|
+
return;
|
|
62
|
+
const fresh = await this._refresher();
|
|
63
|
+
this._browserUrl = fresh._browserUrl;
|
|
64
|
+
this._serviceUrl = fresh._serviceUrl;
|
|
65
|
+
this._token = fresh._token;
|
|
66
|
+
this._expiresAt = fresh._expiresAt;
|
|
67
|
+
}
|
|
68
|
+
/** The signed token, refreshed if near expiry. */
|
|
69
|
+
async token() {
|
|
70
|
+
await this._maybeRefresh();
|
|
71
|
+
return this._token;
|
|
72
|
+
}
|
|
73
|
+
/** The service base URL, refreshed if near expiry. */
|
|
74
|
+
async serviceUrl() {
|
|
75
|
+
await this._maybeRefresh();
|
|
76
|
+
return this._serviceUrl;
|
|
77
|
+
}
|
|
78
|
+
/** The browser URL that authenticates then redirects, refreshed if near expiry. */
|
|
79
|
+
async browserUrl() {
|
|
80
|
+
await this._maybeRefresh();
|
|
81
|
+
return this._browserUrl;
|
|
82
|
+
}
|
|
83
|
+
/** ISO 8601 expiry of the current token, refreshed if near expiry. */
|
|
84
|
+
async expiresAt() {
|
|
85
|
+
await this._maybeRefresh();
|
|
86
|
+
return this._expiresAt;
|
|
87
|
+
}
|
|
88
|
+
/** Fetch a path on the service with the token header injected. */
|
|
89
|
+
async fetch(path = "/", init = {}) {
|
|
90
|
+
const base = (await this.serviceUrl()).replace(/\/+$/, "");
|
|
91
|
+
const url = `${base}/${path.replace(/^\/+/, "")}`;
|
|
92
|
+
const headers = new Headers(init.headers);
|
|
93
|
+
headers.set(exports.SERVICE_TOKEN_HEADER, await this.token());
|
|
94
|
+
return fetch(url, { ...init, headers });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.ServiceUrl = ServiceUrl;
|
|
98
|
+
/**
|
|
99
|
+
* Service URL gated by LangSmith login rather than a token.
|
|
100
|
+
*
|
|
101
|
+
* The grant is durable: no token, no expiry, and only usable from a browser
|
|
102
|
+
* signed in to LangSmith — which is why there is no fetch helper here, a
|
|
103
|
+
* programmatic request cannot satisfy the login.
|
|
104
|
+
*/
|
|
105
|
+
class ServiceLoginUrl {
|
|
106
|
+
constructor(data) {
|
|
107
|
+
/** The URL to open in a browser. */
|
|
108
|
+
Object.defineProperty(this, "url", {
|
|
109
|
+
enumerable: true,
|
|
110
|
+
configurable: true,
|
|
111
|
+
writable: true,
|
|
112
|
+
value: void 0
|
|
113
|
+
});
|
|
114
|
+
/** Who may open it: "restricted" or "workspace". */
|
|
115
|
+
Object.defineProperty(this, "access", {
|
|
116
|
+
enumerable: true,
|
|
117
|
+
configurable: true,
|
|
118
|
+
writable: true,
|
|
119
|
+
value: void 0
|
|
120
|
+
});
|
|
121
|
+
this.url = data.browser_url || data.service_url || "";
|
|
122
|
+
this.access = data.access ?? "";
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.ServiceLoginUrl = ServiceLoginUrl;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** How a service URL is gated. Omit for a minted token. */
|
|
2
|
+
export type ServiceAccess = "restricted" | "workspace";
|
|
3
|
+
/** Header the sandbox router reads the minted service token from. */
|
|
4
|
+
export declare const SERVICE_TOKEN_HEADER = "X-Langsmith-Sandbox-Service-Token";
|
|
5
|
+
export interface ServiceUrlData {
|
|
6
|
+
browser_url?: string;
|
|
7
|
+
service_url?: string;
|
|
8
|
+
token?: string;
|
|
9
|
+
expires_at?: string;
|
|
10
|
+
access?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Service URL carrying a short-lived token, refreshed as it nears expiry.
|
|
14
|
+
*
|
|
15
|
+
* Accessors are async because a refresh is a network call.
|
|
16
|
+
*/
|
|
17
|
+
export declare class ServiceUrl {
|
|
18
|
+
private _browserUrl;
|
|
19
|
+
private _serviceUrl;
|
|
20
|
+
private _token;
|
|
21
|
+
private _expiresAt;
|
|
22
|
+
private readonly _refresher?;
|
|
23
|
+
constructor(data: ServiceUrlData, refresher?: () => Promise<ServiceUrl>);
|
|
24
|
+
private _shouldRefresh;
|
|
25
|
+
private _maybeRefresh;
|
|
26
|
+
/** The signed token, refreshed if near expiry. */
|
|
27
|
+
token(): Promise<string>;
|
|
28
|
+
/** The service base URL, refreshed if near expiry. */
|
|
29
|
+
serviceUrl(): Promise<string>;
|
|
30
|
+
/** The browser URL that authenticates then redirects, refreshed if near expiry. */
|
|
31
|
+
browserUrl(): Promise<string>;
|
|
32
|
+
/** ISO 8601 expiry of the current token, refreshed if near expiry. */
|
|
33
|
+
expiresAt(): Promise<string>;
|
|
34
|
+
/** Fetch a path on the service with the token header injected. */
|
|
35
|
+
fetch(path?: string, init?: RequestInit): Promise<Response>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Service URL gated by LangSmith login rather than a token.
|
|
39
|
+
*
|
|
40
|
+
* The grant is durable: no token, no expiry, and only usable from a browser
|
|
41
|
+
* signed in to LangSmith — which is why there is no fetch helper here, a
|
|
42
|
+
* programmatic request cannot satisfy the login.
|
|
43
|
+
*/
|
|
44
|
+
export declare class ServiceLoginUrl {
|
|
45
|
+
/** The URL to open in a browser. */
|
|
46
|
+
readonly url: string;
|
|
47
|
+
/** Who may open it: "restricted" or "workspace". */
|
|
48
|
+
readonly access: string;
|
|
49
|
+
constructor(data: ServiceUrlData);
|
|
50
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/** Header the sandbox router reads the minted service token from. */
|
|
2
|
+
export const SERVICE_TOKEN_HEADER = "X-Langsmith-Sandbox-Service-Token";
|
|
3
|
+
/** Refresh this many seconds before the token actually expires. */
|
|
4
|
+
const REFRESH_MARGIN_SECONDS = 60;
|
|
5
|
+
/**
|
|
6
|
+
* Service URL carrying a short-lived token, refreshed as it nears expiry.
|
|
7
|
+
*
|
|
8
|
+
* Accessors are async because a refresh is a network call.
|
|
9
|
+
*/
|
|
10
|
+
export class ServiceUrl {
|
|
11
|
+
constructor(data, refresher) {
|
|
12
|
+
Object.defineProperty(this, "_browserUrl", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
configurable: true,
|
|
15
|
+
writable: true,
|
|
16
|
+
value: void 0
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(this, "_serviceUrl", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
configurable: true,
|
|
21
|
+
writable: true,
|
|
22
|
+
value: void 0
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(this, "_token", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
configurable: true,
|
|
27
|
+
writable: true,
|
|
28
|
+
value: void 0
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(this, "_expiresAt", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
configurable: true,
|
|
33
|
+
writable: true,
|
|
34
|
+
value: void 0
|
|
35
|
+
});
|
|
36
|
+
Object.defineProperty(this, "_refresher", {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
configurable: true,
|
|
39
|
+
writable: true,
|
|
40
|
+
value: void 0
|
|
41
|
+
});
|
|
42
|
+
this._browserUrl = data.browser_url ?? "";
|
|
43
|
+
this._serviceUrl = data.service_url ?? "";
|
|
44
|
+
this._token = data.token ?? "";
|
|
45
|
+
this._expiresAt = data.expires_at ?? "";
|
|
46
|
+
this._refresher = refresher;
|
|
47
|
+
}
|
|
48
|
+
_shouldRefresh() {
|
|
49
|
+
if (!this._refresher || !this._expiresAt)
|
|
50
|
+
return false;
|
|
51
|
+
const expires = Date.parse(this._expiresAt);
|
|
52
|
+
if (Number.isNaN(expires))
|
|
53
|
+
return false;
|
|
54
|
+
return (expires - Date.now()) / 1000 <= REFRESH_MARGIN_SECONDS;
|
|
55
|
+
}
|
|
56
|
+
async _maybeRefresh() {
|
|
57
|
+
if (!this._refresher || !this._shouldRefresh())
|
|
58
|
+
return;
|
|
59
|
+
const fresh = await this._refresher();
|
|
60
|
+
this._browserUrl = fresh._browserUrl;
|
|
61
|
+
this._serviceUrl = fresh._serviceUrl;
|
|
62
|
+
this._token = fresh._token;
|
|
63
|
+
this._expiresAt = fresh._expiresAt;
|
|
64
|
+
}
|
|
65
|
+
/** The signed token, refreshed if near expiry. */
|
|
66
|
+
async token() {
|
|
67
|
+
await this._maybeRefresh();
|
|
68
|
+
return this._token;
|
|
69
|
+
}
|
|
70
|
+
/** The service base URL, refreshed if near expiry. */
|
|
71
|
+
async serviceUrl() {
|
|
72
|
+
await this._maybeRefresh();
|
|
73
|
+
return this._serviceUrl;
|
|
74
|
+
}
|
|
75
|
+
/** The browser URL that authenticates then redirects, refreshed if near expiry. */
|
|
76
|
+
async browserUrl() {
|
|
77
|
+
await this._maybeRefresh();
|
|
78
|
+
return this._browserUrl;
|
|
79
|
+
}
|
|
80
|
+
/** ISO 8601 expiry of the current token, refreshed if near expiry. */
|
|
81
|
+
async expiresAt() {
|
|
82
|
+
await this._maybeRefresh();
|
|
83
|
+
return this._expiresAt;
|
|
84
|
+
}
|
|
85
|
+
/** Fetch a path on the service with the token header injected. */
|
|
86
|
+
async fetch(path = "/", init = {}) {
|
|
87
|
+
const base = (await this.serviceUrl()).replace(/\/+$/, "");
|
|
88
|
+
const url = `${base}/${path.replace(/^\/+/, "")}`;
|
|
89
|
+
const headers = new Headers(init.headers);
|
|
90
|
+
headers.set(SERVICE_TOKEN_HEADER, await this.token());
|
|
91
|
+
return fetch(url, { ...init, headers });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Service URL gated by LangSmith login rather than a token.
|
|
96
|
+
*
|
|
97
|
+
* The grant is durable: no token, no expiry, and only usable from a browser
|
|
98
|
+
* signed in to LangSmith — which is why there is no fetch helper here, a
|
|
99
|
+
* programmatic request cannot satisfy the login.
|
|
100
|
+
*/
|
|
101
|
+
export class ServiceLoginUrl {
|
|
102
|
+
constructor(data) {
|
|
103
|
+
/** The URL to open in a browser. */
|
|
104
|
+
Object.defineProperty(this, "url", {
|
|
105
|
+
enumerable: true,
|
|
106
|
+
configurable: true,
|
|
107
|
+
writable: true,
|
|
108
|
+
value: void 0
|
|
109
|
+
});
|
|
110
|
+
/** Who may open it: "restricted" or "workspace". */
|
|
111
|
+
Object.defineProperty(this, "access", {
|
|
112
|
+
enumerable: true,
|
|
113
|
+
configurable: true,
|
|
114
|
+
writable: true,
|
|
115
|
+
value: void 0
|
|
116
|
+
});
|
|
117
|
+
this.url = data.browser_url || data.service_url || "";
|
|
118
|
+
this.access = data.access ?? "";
|
|
119
|
+
}
|
|
120
|
+
}
|
package/dist/sandbox/types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Field names use snake_case to match API response format.
|
|
5
5
|
*/
|
|
6
|
+
import type { AccessDelegation } from "./access_delegation.js";
|
|
6
7
|
/**
|
|
7
8
|
* Result of executing a command in a sandbox.
|
|
8
9
|
*/
|
|
@@ -20,6 +21,164 @@ export interface ResourceStatus {
|
|
|
20
21
|
/** Human-readable details when failed. */
|
|
21
22
|
status_message?: string;
|
|
22
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* The user, working directory and environment commands run with.
|
|
26
|
+
*
|
|
27
|
+
* Mirrors `docker run -u / -w / -e`: `user` and `work_dir` replace the layer
|
|
28
|
+
* below, `env_vars` merge into it key by key. It applies at three points, each
|
|
29
|
+
* layered over the one before -- the snapshot, the sandbox, and a single
|
|
30
|
+
* command.
|
|
31
|
+
*/
|
|
32
|
+
export interface RunConfig {
|
|
33
|
+
/**
|
|
34
|
+
* Account to run as: `name`, `uid`, `name:group` or `uid:gid`. Defaults to
|
|
35
|
+
* the Docker image's `USER`.
|
|
36
|
+
*/
|
|
37
|
+
user?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Absolute working directory. Defaults to the image's `WORKDIR`. A relative
|
|
40
|
+
* path is rejected by the server.
|
|
41
|
+
*/
|
|
42
|
+
work_dir?: string;
|
|
43
|
+
/** Environment variables, merged over the layer below. */
|
|
44
|
+
env_vars?: Record<string, string>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* One filesystem entry returned by `sandbox.glob()`.
|
|
48
|
+
*/
|
|
49
|
+
export interface FileInfo {
|
|
50
|
+
/** Absolute path of the entry. */
|
|
51
|
+
path: string;
|
|
52
|
+
/** True for a directory. */
|
|
53
|
+
is_dir: boolean;
|
|
54
|
+
/** Size in bytes. */
|
|
55
|
+
size_bytes: number;
|
|
56
|
+
/** RFC 3339 modification timestamp. */
|
|
57
|
+
modified_at?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Entries matching a glob pattern.
|
|
61
|
+
*/
|
|
62
|
+
export interface GlobResult {
|
|
63
|
+
matches: FileInfo[];
|
|
64
|
+
/**
|
|
65
|
+
* True when the server hit its result cap or deadline, so the search is a
|
|
66
|
+
* partial answer worth refining.
|
|
67
|
+
*/
|
|
68
|
+
truncated: boolean;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* One matching line found by `sandbox.grep()`.
|
|
72
|
+
*/
|
|
73
|
+
export interface GrepMatch {
|
|
74
|
+
/** Absolute path of the file the match was found in. */
|
|
75
|
+
path: string;
|
|
76
|
+
/** 1-based line number. */
|
|
77
|
+
line: number;
|
|
78
|
+
/** The matching line's text. */
|
|
79
|
+
text: string;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Lines matching a literal search.
|
|
83
|
+
*/
|
|
84
|
+
export interface GrepResult {
|
|
85
|
+
matches: GrepMatch[];
|
|
86
|
+
/** True when the server hit its result cap or deadline. */
|
|
87
|
+
truncated: boolean;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Options for the read-only filesystem search operations.
|
|
91
|
+
*/
|
|
92
|
+
export interface GlobOptions {
|
|
93
|
+
/** Maximum matches to return. */
|
|
94
|
+
limit?: number;
|
|
95
|
+
/** Request timeout in seconds. */
|
|
96
|
+
timeout?: number;
|
|
97
|
+
headers?: Record<string, string>;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Options for `sandbox.grep()`.
|
|
101
|
+
*/
|
|
102
|
+
export interface GrepOptions extends GlobOptions {
|
|
103
|
+
/**
|
|
104
|
+
* Restrict which files are searched. A bare `*.py` matches by basename at
|
|
105
|
+
* any depth; a pattern containing `/` or `**` matches the path relative to
|
|
106
|
+
* the search root.
|
|
107
|
+
*/
|
|
108
|
+
glob?: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* What a `HEAD` on a sandbox file reports, without transferring it.
|
|
112
|
+
*/
|
|
113
|
+
export interface FileStat {
|
|
114
|
+
/** The file's size. */
|
|
115
|
+
size_bytes: number;
|
|
116
|
+
/**
|
|
117
|
+
* Strong validator for the current contents. Opaque -- compare it, never
|
|
118
|
+
* parse it. Pass it back as `ifRange` to resume a download safely, or as
|
|
119
|
+
* `ifNoneMatch` to poll for a change.
|
|
120
|
+
*/
|
|
121
|
+
etag?: string;
|
|
122
|
+
/** HTTP-date of the last modification, second-resolution. */
|
|
123
|
+
last_modified?: string;
|
|
124
|
+
/** The server's content type for the file. */
|
|
125
|
+
content_type?: string;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Bytes returned by a ranged read, and where they sit in the file.
|
|
129
|
+
*/
|
|
130
|
+
export interface FileChunk {
|
|
131
|
+
/** The bytes returned. Empty when `unchanged` is true. */
|
|
132
|
+
content: Uint8Array;
|
|
133
|
+
/**
|
|
134
|
+
* Validator for the version these bytes came from. Pass it as `ifRange` on
|
|
135
|
+
* the next chunk so a rewrite restarts the read instead of splicing two
|
|
136
|
+
* versions together.
|
|
137
|
+
*/
|
|
138
|
+
etag?: string;
|
|
139
|
+
/** The file's full size, or undefined when the server did not report it. */
|
|
140
|
+
total_bytes?: number;
|
|
141
|
+
/** Offset of the first byte returned. */
|
|
142
|
+
start: number;
|
|
143
|
+
/**
|
|
144
|
+
* True when the server answered 206 with only part of the file. A false
|
|
145
|
+
* here after a ranged request means the file changed and the server sent it
|
|
146
|
+
* whole -- restart from zero.
|
|
147
|
+
*/
|
|
148
|
+
partial: boolean;
|
|
149
|
+
/**
|
|
150
|
+
* True when the caller passed `ifNoneMatch` and the file still matches it.
|
|
151
|
+
* No bytes are returned.
|
|
152
|
+
*/
|
|
153
|
+
unchanged: boolean;
|
|
154
|
+
/** HTTP-date of the last modification. */
|
|
155
|
+
last_modified?: string;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Options for a ranged read. Provide `start` (with optional `end`), or
|
|
159
|
+
* `suffixBytes`.
|
|
160
|
+
*/
|
|
161
|
+
export interface ReadRangeOptions {
|
|
162
|
+
/** First byte to return. With `end`, an inclusive range. */
|
|
163
|
+
start?: number;
|
|
164
|
+
/** Last byte to return, inclusive. Clamped to EOF rather than rejected. */
|
|
165
|
+
end?: number;
|
|
166
|
+
/** Return the last N bytes. Mutually exclusive with `start`/`end`. */
|
|
167
|
+
suffixBytes?: number;
|
|
168
|
+
/**
|
|
169
|
+
* An `ETag` from an earlier chunk. The range is honored only while the file
|
|
170
|
+
* still matches it.
|
|
171
|
+
*/
|
|
172
|
+
ifRange?: string;
|
|
173
|
+
/**
|
|
174
|
+
* An `ETag` the caller already holds. An unchanged file answers with
|
|
175
|
+
* `unchanged: true` and no bytes.
|
|
176
|
+
*/
|
|
177
|
+
ifNoneMatch?: string;
|
|
178
|
+
/** Request timeout in seconds. */
|
|
179
|
+
timeout?: number;
|
|
180
|
+
headers?: Record<string, string>;
|
|
181
|
+
}
|
|
23
182
|
/**
|
|
24
183
|
* Represents a sandbox snapshot.
|
|
25
184
|
*
|
|
@@ -41,6 +200,13 @@ export interface Snapshot {
|
|
|
41
200
|
registry_id?: string;
|
|
42
201
|
created_at?: string;
|
|
43
202
|
updated_at?: string;
|
|
203
|
+
/**
|
|
204
|
+
* User, working directory and environment sandboxes built from this
|
|
205
|
+
* snapshot boot with, resolved from the Docker image at build time. Absent
|
|
206
|
+
* on snapshots built before the server recorded it, which boot as root with
|
|
207
|
+
* no image environment.
|
|
208
|
+
*/
|
|
209
|
+
run_config?: RunConfig;
|
|
44
210
|
}
|
|
45
211
|
/**
|
|
46
212
|
* Data representing a sandbox instance from the API.
|
|
@@ -79,6 +245,16 @@ export interface SandboxData {
|
|
|
79
245
|
mem_bytes?: number;
|
|
80
246
|
/** Root filesystem capacity in bytes. */
|
|
81
247
|
fs_capacity_bytes?: number;
|
|
248
|
+
/**
|
|
249
|
+
* User, working directory and environment the sandbox's commands run with.
|
|
250
|
+
* Absent on sandboxes created before the server recorded it.
|
|
251
|
+
*/
|
|
252
|
+
run_config?: RunConfig;
|
|
253
|
+
/**
|
|
254
|
+
* LangSmith access granted to code inside the sandbox. Absent when it has
|
|
255
|
+
* none.
|
|
256
|
+
*/
|
|
257
|
+
access_delegation?: AccessDelegation;
|
|
82
258
|
}
|
|
83
259
|
/**
|
|
84
260
|
* Configuration options for the SandboxClient.
|
|
@@ -170,6 +346,10 @@ export interface WsRunOptions {
|
|
|
170
346
|
ttlSeconds?: number;
|
|
171
347
|
/** Whether to allocate a PTY. */
|
|
172
348
|
pty?: boolean;
|
|
349
|
+
/** Per-command user, working directory and environment. */
|
|
350
|
+
runConfig?: RunConfig;
|
|
351
|
+
/** Half-close the spawned process's stdin so it reads EOF. */
|
|
352
|
+
closeStdin?: boolean;
|
|
173
353
|
/**
|
|
174
354
|
* Additional headers attached to the WebSocket upgrade request. Merged on
|
|
175
355
|
* top of any default headers the SandboxClient was constructed with.
|
|
@@ -186,12 +366,30 @@ export interface RunOptions {
|
|
|
186
366
|
timeout?: number;
|
|
187
367
|
/**
|
|
188
368
|
* Environment variables to set for the command.
|
|
369
|
+
*
|
|
370
|
+
* @deprecated Use `runConfig.env_vars`. The two cannot be combined.
|
|
189
371
|
*/
|
|
190
372
|
env?: Record<string, string>;
|
|
191
373
|
/**
|
|
192
374
|
* Working directory for command execution.
|
|
375
|
+
*
|
|
376
|
+
* @deprecated Use `runConfig.work_dir`. The two cannot be combined.
|
|
193
377
|
*/
|
|
194
378
|
cwd?: string;
|
|
379
|
+
/**
|
|
380
|
+
* User, working directory and environment for this one command, layered
|
|
381
|
+
* over the sandbox's own.
|
|
382
|
+
*/
|
|
383
|
+
runConfig?: RunConfig;
|
|
384
|
+
/**
|
|
385
|
+
* Half-close the command's stdin so a command that reads it sees EOF
|
|
386
|
+
* rather than blocking until the timeout. Defaults to true for a non-PTY
|
|
387
|
+
* command, which means `sendInput()` on the returned handle throws unless
|
|
388
|
+
* this is set to false. Ignored under `pty: true`, where input and output
|
|
389
|
+
* share one terminal file descriptor -- send an EOT byte (0x04) as input
|
|
390
|
+
* instead.
|
|
391
|
+
*/
|
|
392
|
+
closeInput?: boolean;
|
|
195
393
|
/**
|
|
196
394
|
* Shell to use for command execution. Defaults to "/bin/bash".
|
|
197
395
|
*/
|
|
@@ -262,8 +460,20 @@ export interface SandboxProxySecret {
|
|
|
262
460
|
/** Workspace secret reference or opaque secret value. */
|
|
263
461
|
value: string;
|
|
264
462
|
}
|
|
463
|
+
/** Static credentials used by the sandbox proxy signer. */
|
|
464
|
+
interface AwsStaticAuthConfig {
|
|
465
|
+
access_key_id: SandboxProxySecret;
|
|
466
|
+
secret_access_key: SandboxProxySecret;
|
|
467
|
+
role_arn?: "";
|
|
468
|
+
}
|
|
469
|
+
/** IAM role assumed and renewed by LangSmith, outside the sandbox. */
|
|
470
|
+
interface AwsRoleAuthConfig {
|
|
471
|
+
role_arn: string;
|
|
472
|
+
access_key_id?: never;
|
|
473
|
+
secret_access_key?: never;
|
|
474
|
+
}
|
|
265
475
|
/** AWS auth rule for sandbox proxy SigV4 signing. */
|
|
266
|
-
export interface SandboxAwsAuthRule {
|
|
476
|
+
export interface SandboxAwsAuthRule<Auth extends AwsStaticAuthConfig | AwsRoleAuthConfig = AwsStaticAuthConfig | AwsRoleAuthConfig> {
|
|
267
477
|
/** Rule name. */
|
|
268
478
|
name: string;
|
|
269
479
|
/** AWS auth rules are matched by the sandbox proxy's AWS endpoint matcher. */
|
|
@@ -277,11 +487,8 @@ export interface SandboxAwsAuthRule {
|
|
|
277
487
|
* the wire.
|
|
278
488
|
*/
|
|
279
489
|
env_vars?: Record<string, string>;
|
|
280
|
-
/**
|
|
281
|
-
aws:
|
|
282
|
-
access_key_id: SandboxProxySecret;
|
|
283
|
-
secret_access_key: SandboxProxySecret;
|
|
284
|
-
};
|
|
490
|
+
/** Exclusive static credentials or IAM-role descriptor. */
|
|
491
|
+
aws: Auth;
|
|
285
492
|
}
|
|
286
493
|
/** GCP auth rule for sandbox proxy OAuth bearer injection. */
|
|
287
494
|
export interface SandboxGcpAuthRule {
|
|
@@ -314,7 +521,7 @@ export type SandboxProxyRule = SandboxAwsAuthRule | SandboxGcpAuthRule | Record<
|
|
|
314
521
|
export interface SandboxProxyConfig {
|
|
315
522
|
/** Header-injection rules keyed by host pattern. */
|
|
316
523
|
rules?: SandboxProxyRule[];
|
|
317
|
-
/**
|
|
524
|
+
/** @deprecated Ignored by the server. The sandbox runtime has no proxy bypass list. */
|
|
318
525
|
no_proxy?: string[];
|
|
319
526
|
/** Allow/deny list enforced at the proxy sidecar. */
|
|
320
527
|
access_control?: SandboxAccessControl;
|
|
@@ -427,6 +634,7 @@ export type SandboxMount = S3MountSpec | GCSMountSpec | GitMountSpec | ContextHu
|
|
|
427
634
|
export interface SandboxAwsMountAuthConfig {
|
|
428
635
|
access_key_id: SandboxProxySecret;
|
|
429
636
|
secret_access_key: SandboxProxySecret;
|
|
637
|
+
role_arn?: "";
|
|
430
638
|
}
|
|
431
639
|
/** GCP credentials used by the backend to authenticate GCS mounts. */
|
|
432
640
|
export interface SandboxGcpMountAuthConfig {
|
|
@@ -434,7 +642,8 @@ export interface SandboxGcpMountAuthConfig {
|
|
|
434
642
|
}
|
|
435
643
|
/** Provider auth blocks for sandbox mounts. */
|
|
436
644
|
export interface SandboxMountAuthConfig {
|
|
437
|
-
|
|
645
|
+
/** IAM roles here are restricted to this sandbox's S3 mount scopes. */
|
|
646
|
+
aws?: SandboxAwsMountAuthConfig | AwsRoleAuthConfig;
|
|
438
647
|
gcp?: SandboxGcpMountAuthConfig;
|
|
439
648
|
}
|
|
440
649
|
/** Provider auth helper output accepted by mountConfig. */
|
|
@@ -507,6 +716,21 @@ export interface CreateSandboxOptions {
|
|
|
507
716
|
* auth.
|
|
508
717
|
*/
|
|
509
718
|
proxyConfig?: SandboxProxyConfig;
|
|
719
|
+
/**
|
|
720
|
+
* User, working directory and environment the sandbox boots with,
|
|
721
|
+
* overriding the snapshot's: `user` and `work_dir` replace, `env_vars`
|
|
722
|
+
* merge. The sandbox's own env vars remain a layer above this one.
|
|
723
|
+
*/
|
|
724
|
+
runConfig?: RunConfig;
|
|
725
|
+
/**
|
|
726
|
+
* Grant letting code inside the sandbox call the LangSmith API as you, with
|
|
727
|
+
* no API key of its own. `{ mode: "INHERIT" }` grants everything you can do;
|
|
728
|
+
* `{ mode: "EXPLICIT", permissions: [...] }` grants only the permissions
|
|
729
|
+
* listed, each of which you must already hold. The grant belongs to the
|
|
730
|
+
* sandbox, so anyone who can exec into it can make calls under it. Omit for
|
|
731
|
+
* no access.
|
|
732
|
+
*/
|
|
733
|
+
accessDelegation?: AccessDelegation;
|
|
510
734
|
}
|
|
511
735
|
/**
|
|
512
736
|
* Options for creating a snapshot from a Docker image.
|
|
@@ -514,6 +738,13 @@ export interface CreateSandboxOptions {
|
|
|
514
738
|
export interface CreateSnapshotOptions {
|
|
515
739
|
/** Private registry ID. */
|
|
516
740
|
registryId?: string;
|
|
741
|
+
/**
|
|
742
|
+
* Override the Docker image's `USER`, `WORKDIR` and `ENV` for sandboxes
|
|
743
|
+
* built from this snapshot: `user` and `work_dir` replace the image's,
|
|
744
|
+
* `env_vars` merge over it key by key. Omitting it adopts the image's own
|
|
745
|
+
* configuration. Pass `{ user: "0" }` to keep running as root.
|
|
746
|
+
*/
|
|
747
|
+
runConfig?: RunConfig;
|
|
517
748
|
/** Timeout in seconds when waiting for ready. Default: 60. */
|
|
518
749
|
timeout?: number;
|
|
519
750
|
/** AbortSignal for cancellation. */
|
|
@@ -554,6 +785,11 @@ export interface CaptureSnapshotOptions {
|
|
|
554
785
|
dockerImage?: string;
|
|
555
786
|
/** Filesystem capacity in bytes for Docker image export. */
|
|
556
787
|
fsCapacityBytes?: number;
|
|
788
|
+
/**
|
|
789
|
+
* Override applied over the configuration the captured sandbox was running
|
|
790
|
+
* with: `user` and `work_dir` replace, `env_vars` merge.
|
|
791
|
+
*/
|
|
792
|
+
runConfig?: RunConfig;
|
|
557
793
|
/** Timeout in seconds when waiting for ready. Default: 60. */
|
|
558
794
|
timeout?: number;
|
|
559
795
|
/** AbortSignal for cancellation. */
|
|
@@ -669,6 +905,13 @@ export interface UpdateSandboxOptions {
|
|
|
669
905
|
* `ready`; start a stopped one first.
|
|
670
906
|
*/
|
|
671
907
|
proxyConfig?: SandboxProxyConfig;
|
|
908
|
+
/**
|
|
909
|
+
* Merge into the sandbox's stored run configuration: `user` and `work_dir`
|
|
910
|
+
* replace, `env_vars` merge. Takes effect for subsequent commands; commands
|
|
911
|
+
* already running are unaffected, and a stopped sandbox picks it up on its
|
|
912
|
+
* next start. Omitting it leaves the stored value untouched.
|
|
913
|
+
*/
|
|
914
|
+
runConfig?: RunConfig;
|
|
672
915
|
}
|
|
673
916
|
/**
|
|
674
917
|
* Options for waiting for a sandbox to become ready.
|