@giselles-ai/sandkit 0.1.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/LICENSE +21 -0
- package/README.md +163 -0
- package/dist/adapters/drizzle.d.ts +83 -0
- package/dist/adapters/drizzle.js +9 -0
- package/dist/adapters/drizzle.js.map +1 -0
- package/dist/adapters/memory.d.ts +6 -0
- package/dist/adapters/memory.js +8 -0
- package/dist/adapters/memory.js.map +1 -0
- package/dist/adapters/sqlite-bun.d.ts +7 -0
- package/dist/adapters/sqlite-bun.js +8 -0
- package/dist/adapters/sqlite-bun.js.map +1 -0
- package/dist/bin.js +697 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-7DLK7LOM.js +44 -0
- package/dist/chunk-7DLK7LOM.js.map +1 -0
- package/dist/chunk-BDPTYR6V.js +407 -0
- package/dist/chunk-BDPTYR6V.js.map +1 -0
- package/dist/chunk-CSOBTLWV.js +202 -0
- package/dist/chunk-CSOBTLWV.js.map +1 -0
- package/dist/chunk-DLGUA3H7.js +9 -0
- package/dist/chunk-DLGUA3H7.js.map +1 -0
- package/dist/chunk-FSDVHEEX.js +45 -0
- package/dist/chunk-FSDVHEEX.js.map +1 -0
- package/dist/chunk-HVYCAAZQ.js +25 -0
- package/dist/chunk-HVYCAAZQ.js.map +1 -0
- package/dist/chunk-LC3IYBAL.js +100 -0
- package/dist/chunk-LC3IYBAL.js.map +1 -0
- package/dist/chunk-REGOUXVI.js +58 -0
- package/dist/chunk-REGOUXVI.js.map +1 -0
- package/dist/chunk-RMMOQD5Y.js +211 -0
- package/dist/chunk-RMMOQD5Y.js.map +1 -0
- package/dist/chunk-UDFWES6J.js +486 -0
- package/dist/chunk-UDFWES6J.js.map +1 -0
- package/dist/chunk-VISDS5T7.js +202 -0
- package/dist/chunk-VISDS5T7.js.map +1 -0
- package/dist/chunk-XM4HGRXW.js +37 -0
- package/dist/chunk-XM4HGRXW.js.map +1 -0
- package/dist/cli/index.d.ts +19 -0
- package/dist/cli/index.js +397 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.js +1102 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/mock.d.ts +19 -0
- package/dist/integrations/mock.js +207 -0
- package/dist/integrations/mock.js.map +1 -0
- package/dist/integrations/vercel.d.ts +7 -0
- package/dist/integrations/vercel.js +400 -0
- package/dist/integrations/vercel.js.map +1 -0
- package/dist/policies/ai-gateway.d.ts +15 -0
- package/dist/policies/ai-gateway.js +12 -0
- package/dist/policies/ai-gateway.js.map +1 -0
- package/dist/policies/codex.d.ts +10 -0
- package/dist/policies/codex.js +12 -0
- package/dist/policies/codex.js.map +1 -0
- package/dist/policies/gemini.d.ts +10 -0
- package/dist/policies/gemini.js +12 -0
- package/dist/policies/gemini.js.map +1 -0
- package/dist/schema/index.d.ts +60 -0
- package/dist/schema/index.js +31 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/types-BCgprbo8.d.ts +47 -0
- package/dist/types-BEKQnjeb.d.ts +139 -0
- package/dist/types-Cy36bS1j.d.ts +138 -0
- package/package.json +126 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Giselle
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Sandkit
|
|
2
|
+
|
|
3
|
+
Sandkit makes Vercel Sandbox stateful.
|
|
4
|
+
|
|
5
|
+
Sandkit adds workspace state, session management, durable command execution, and resumable sandbox workflows to Vercel Sandbox.
|
|
6
|
+
|
|
7
|
+
It keeps two paths explicit:
|
|
8
|
+
|
|
9
|
+
- `workspace.sandbox.runCommand(...)` for durable, one-command-at-a-time work
|
|
10
|
+
- `openSession()` / `attachSession()` for a live leased sandbox when you need an interactive process
|
|
11
|
+
|
|
12
|
+
An active session is an exclusive workspace lease. While a live session is open, `runCommand()` is unavailable until you attach to that session or commit it.
|
|
13
|
+
|
|
14
|
+
Provider-specific behavior still matters, but the public API stays centered on workspaces, policies, and durable state.
|
|
15
|
+
|
|
16
|
+
## Problem
|
|
17
|
+
|
|
18
|
+
Vercel Sandbox is ephemeral by design. It does not give you durable workspaces, session lifecycle, or a clear boundary between one-shot commands and live attached execution.
|
|
19
|
+
|
|
20
|
+
- No built-in workspace identity or durable workspace state
|
|
21
|
+
- No session management abstraction for live attach / resume
|
|
22
|
+
- No durable command boundary for one-command-at-a-time work
|
|
23
|
+
- Teams end up rebuilding the same sandbox state and lifecycle layer around jobs, agents, and recovery flows
|
|
24
|
+
|
|
25
|
+
## Solution
|
|
26
|
+
|
|
27
|
+
Sandkit adds a workspace state layer on top of Vercel Sandbox:
|
|
28
|
+
|
|
29
|
+
- Persistent workspaces for sandbox state management
|
|
30
|
+
- Live session lifecycle with explicit attach / commit semantics
|
|
31
|
+
- Durable command execution through `runCommand(...)` for committed work
|
|
32
|
+
- Policy controls that stay part of workspace state
|
|
33
|
+
- Resumable sandbox workflows for long-running apps and control planes
|
|
34
|
+
|
|
35
|
+
## Positioning
|
|
36
|
+
|
|
37
|
+
| Tool | Responsibility |
|
|
38
|
+
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
39
|
+
| Vercel Sandbox | Ephemeral execution environment |
|
|
40
|
+
| Sandkit | State and lifecycle layer for sandboxed execution: workspaces, session lifecycle, durable command boundaries, and resume |
|
|
41
|
+
| Workflow engines / app control planes | Decide when and why work runs |
|
|
42
|
+
|
|
43
|
+
## Primary Use Case: Persistent Workspaces for AI Coding Agents
|
|
44
|
+
|
|
45
|
+
Sandkit is especially useful when an agent or long-running sandbox app needs to keep a workspace alive across runs, attach to a live process, expose a public URL, and commit progress durably.
|
|
46
|
+
|
|
47
|
+
## Install
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
npm install @giselles-ai/sandkit
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
With Drizzle:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
npm install @giselles-ai/sandkit drizzle-orm
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Quick Start
|
|
60
|
+
|
|
61
|
+
> Migration note: `sandkit(...)` was renamed to `createSandkit(...)` and this package is not yet aliased.
|
|
62
|
+
> Callers must update imports and call sites from `sandkit` to `createSandkit`.
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
import { Database } from "bun:sqlite";
|
|
66
|
+
|
|
67
|
+
import { createSandkit } from "@giselles-ai/sandkit";
|
|
68
|
+
import { createBunSqliteAdapter } from "@giselles-ai/sandkit/adapters/sqlite-bun";
|
|
69
|
+
import { vercelSandbox } from "@giselles-ai/sandkit/integrations/vercel";
|
|
70
|
+
|
|
71
|
+
const database = new Database("./sandkit.sqlite");
|
|
72
|
+
const workspaceAdapter = createBunSqliteAdapter(database);
|
|
73
|
+
|
|
74
|
+
const sandkit = createSandkit({
|
|
75
|
+
database: workspaceAdapter,
|
|
76
|
+
sandbox: vercelSandbox({
|
|
77
|
+
defaultTimeout: 60_000,
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const workspace = await sandkit.createWorkspace({
|
|
82
|
+
name: "hello-sandkit",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
await workspace.sandbox.runCommand({
|
|
86
|
+
command: "sh",
|
|
87
|
+
args: ["-lc", "echo 'hello world' > ./hello.txt"],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const result = await workspace.sandbox.runCommand({
|
|
91
|
+
command: "cat",
|
|
92
|
+
args: ["./hello.txt"],
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
console.log(result.stdout.trim());
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Set `VERCEL_OIDC_TOKEN` for local runs or `VERCEL_ACCESS_TOKEN` in CI before creating a Vercel-backed sandbox.
|
|
99
|
+
|
|
100
|
+
Declare `exposedPorts` on `createWorkspace({ sandbox: ... })` only when you need a live session URL. `defaultTimeout` is the provider-level lease default; override a specific live session with `openSession({ timeoutMs })`.
|
|
101
|
+
|
|
102
|
+
## Setup bootstrap
|
|
103
|
+
|
|
104
|
+
Pass setup to `createSandkit({ setup })` to seed a shared durable state used by all workspaces on the same adapter.
|
|
105
|
+
Each workspace starts from that shared bootstrap snapshot when no workspace-specific durable state exists.
|
|
106
|
+
Sandkit persists one shared bootstrap state per adapter and bootstrap definition (command + args + explicit setup policy), runs setup once per unique bootstrap definition, and reuses the matching state for subsequent workspaces.
|
|
107
|
+
If a shared bootstrap state is stale or unusable, Sandkit re-runs setup and persists a replacement.
|
|
108
|
+
By default setup runs under the workspace policy; set `setup.policy` when bootstrap needs broader access than steady-state execution.
|
|
109
|
+
Because setup becomes shared durable state, `setup.policy` must also be durable: explicit secret-bearing policies are rejected there.
|
|
110
|
+
|
|
111
|
+
`setup` durability is adapter-backed. With a persistent adapter such as Bun SQLite or Drizzle, the shared bootstrap survives process restarts. With the default in-memory adapter, it does not.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { createSandkit, allowAll } from "@giselles-ai/sandkit";
|
|
115
|
+
import { vercelSandbox } from "@giselles-ai/sandkit/integrations/vercel";
|
|
116
|
+
|
|
117
|
+
const sandkit = createSandkit({
|
|
118
|
+
sandbox: vercelSandbox(),
|
|
119
|
+
setup: {
|
|
120
|
+
command: "sh",
|
|
121
|
+
args: ["-lc", "npm ci"],
|
|
122
|
+
policy: allowAll(),
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
const workspace = await sandkit.createWorkspace({
|
|
127
|
+
name: "bootstrapped-workspace",
|
|
128
|
+
});
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Configuration
|
|
132
|
+
|
|
133
|
+
Provide a `sandbox` provider explicitly (for example `vercelSandbox(...)`).
|
|
134
|
+
If you do not pass `database`, Sandkit defaults to the in-memory adapter. That default is useful for local tests and internal development, but the primary published usage is an explicit Vercel provider plus a persistent adapter.
|
|
135
|
+
|
|
136
|
+
## Policies
|
|
137
|
+
|
|
138
|
+
- `codex()` reads `CODEX_API_KEY`
|
|
139
|
+
- `gemini()` reads `GEMINI_API_KEY`
|
|
140
|
+
- `github()` reads `GITHUB_TOKEN`
|
|
141
|
+
- `aiGateway()` reads `AI_GATEWAY_API_KEY` from host env and allows the hostname (plus wildcard) from `AI_GATEWAY_BASE_URL`.
|
|
142
|
+
`AI_GATEWAY_BASE_URL` ports are ignored for allow-listing; only host/domain matches are used.
|
|
143
|
+
|
|
144
|
+
Durable default policy belongs to the workspace: use `createWorkspace({ policy: ... })` when you create it, or `workspace.setPolicy(...)` later. Pass `policy` to `runCommand(...)` for one-off overrides.
|
|
145
|
+
|
|
146
|
+
## Schema Generation
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
npx @giselles-ai/sandkit generate --adapter drizzle --provider sqlite
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
If the project already has a Drizzle setup, provider discovery can infer the dialect:
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
npx @giselles-ai/sandkit generate
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Examples
|
|
159
|
+
|
|
160
|
+
- `examples/sandbox-openclaw`
|
|
161
|
+
- `smoke/drizzle-sample`
|
|
162
|
+
|
|
163
|
+
Repository: [github.com/giselles-ai/sandkit](https://github.com/giselles-ai/sandkit)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { SQLWrapper } from 'drizzle-orm';
|
|
2
|
+
import { a as SandkitAdapter } from '../types-Cy36bS1j.js';
|
|
3
|
+
import '../types-BCgprbo8.js';
|
|
4
|
+
|
|
5
|
+
interface DrizzleWorkspaceTableShape {
|
|
6
|
+
readonly id: SQLWrapper;
|
|
7
|
+
readonly name: unknown;
|
|
8
|
+
readonly metadata: unknown;
|
|
9
|
+
readonly status: unknown;
|
|
10
|
+
readonly sandboxId: unknown;
|
|
11
|
+
readonly lastResumedAt: unknown;
|
|
12
|
+
readonly createdAt: unknown;
|
|
13
|
+
readonly updatedAt: unknown;
|
|
14
|
+
}
|
|
15
|
+
interface DrizzleRunTableShape {
|
|
16
|
+
readonly id: SQLWrapper;
|
|
17
|
+
readonly workspace_id: unknown;
|
|
18
|
+
readonly provider: unknown;
|
|
19
|
+
readonly execution_target_id: unknown;
|
|
20
|
+
readonly command: unknown;
|
|
21
|
+
readonly args: unknown;
|
|
22
|
+
readonly status: unknown;
|
|
23
|
+
readonly policy_snapshot_id: unknown;
|
|
24
|
+
readonly provider_commit: unknown;
|
|
25
|
+
readonly exit_code: unknown;
|
|
26
|
+
readonly stdout: unknown;
|
|
27
|
+
readonly stderr: unknown;
|
|
28
|
+
readonly started_at: unknown;
|
|
29
|
+
readonly finished_at: unknown;
|
|
30
|
+
}
|
|
31
|
+
interface DrizzlePolicySnapshotTableShape {
|
|
32
|
+
readonly id: SQLWrapper;
|
|
33
|
+
readonly workspace_id: unknown;
|
|
34
|
+
readonly policy_id: unknown;
|
|
35
|
+
readonly config: unknown;
|
|
36
|
+
readonly created_at: unknown;
|
|
37
|
+
}
|
|
38
|
+
interface DrizzleSetupStateTableShape {
|
|
39
|
+
readonly id: SQLWrapper;
|
|
40
|
+
readonly state: unknown;
|
|
41
|
+
readonly createdAt: unknown;
|
|
42
|
+
readonly updatedAt: unknown;
|
|
43
|
+
}
|
|
44
|
+
interface DrizzleSchemaMap {
|
|
45
|
+
[key: string]: unknown;
|
|
46
|
+
}
|
|
47
|
+
interface DrizzleMetadata {
|
|
48
|
+
readonly fullSchema?: DrizzleSchemaMap;
|
|
49
|
+
}
|
|
50
|
+
interface DrizzleDatabaseLike {
|
|
51
|
+
select(): {
|
|
52
|
+
from(table: object): {
|
|
53
|
+
where(condition: unknown): {
|
|
54
|
+
limit(limit: number): Promise<unknown[]>;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
insert(table: object): {
|
|
59
|
+
values(value: Record<string, unknown>): Promise<unknown>;
|
|
60
|
+
};
|
|
61
|
+
update(table: object): {
|
|
62
|
+
set(value: Record<string, unknown>): {
|
|
63
|
+
where(condition: unknown): Promise<unknown>;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
delete(table: object): {
|
|
67
|
+
where(condition: unknown): Promise<unknown>;
|
|
68
|
+
};
|
|
69
|
+
readonly _?: DrizzleMetadata;
|
|
70
|
+
}
|
|
71
|
+
interface DrizzleAdapterOptions<TWorkspaces extends DrizzleWorkspaceTableShape, TRuns extends DrizzleRunTableShape, TPolicySnapshots extends DrizzlePolicySnapshotTableShape, TSetupStates extends DrizzleSetupStateTableShape = DrizzleSetupStateTableShape> {
|
|
72
|
+
provider: "sqlite" | "postgresql" | "mysql";
|
|
73
|
+
workspaces?: TWorkspaces;
|
|
74
|
+
runs?: TRuns;
|
|
75
|
+
policySnapshots?: TPolicySnapshots;
|
|
76
|
+
setupStates?: TSetupStates;
|
|
77
|
+
id?: string;
|
|
78
|
+
}
|
|
79
|
+
declare function drizzleAdapter<TWorkspaces extends DrizzleWorkspaceTableShape, TRuns extends DrizzleRunTableShape, TPolicySnapshots extends DrizzlePolicySnapshotTableShape, TSetupStates extends DrizzleSetupStateTableShape = DrizzleSetupStateTableShape>(db: DrizzleDatabaseLike, options: DrizzleAdapterOptions<TWorkspaces, TRuns, TPolicySnapshots, TSetupStates>): SandkitAdapter;
|
|
80
|
+
type DrizzleWorkspaceTable = DrizzleWorkspaceTableShape;
|
|
81
|
+
type DrizzleRunTable = DrizzleRunTableShape;
|
|
82
|
+
|
|
83
|
+
export { type DrizzleAdapterOptions, type DrizzleRunTable, type DrizzleWorkspaceTable, drizzleAdapter };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|