@sealant/sdk 0.16.0 → 0.18.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/README.md +58 -2
- package/dist/effect/api-client.d.ts +18 -2
- package/dist/internal/blueprint.js +63 -2
- package/dist/types.d.ts +67 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -105,8 +105,9 @@ and injects them at launch.
|
|
|
105
105
|
|
|
106
106
|
## Workspace tools and services
|
|
107
107
|
|
|
108
|
-
Choose a supported operating-system family
|
|
109
|
-
that need runtime support rather than a
|
|
108
|
+
Choose a supported operating-system family — `fedora` (the default), `arch`, `nix`, or `ubuntu` —
|
|
109
|
+
request portable package names, and opt into services that need runtime support rather than a
|
|
110
|
+
package install:
|
|
110
111
|
|
|
111
112
|
```ts
|
|
112
113
|
const workspace = await sealant.workspaces.create({
|
|
@@ -123,6 +124,61 @@ const workspace = await sealant.workspaces.create({
|
|
|
123
124
|
rootless daemon at launch. The workspace receives `DOCKER_HOST`; Sealant never mounts the host
|
|
124
125
|
Docker socket. GitHub credentials provide both `GH_TOKEN` and `GITHUB_TOKEN` to the workspace.
|
|
125
126
|
|
|
127
|
+
## Dotfiles and shell
|
|
128
|
+
|
|
129
|
+
Bring your own environment: a login shell and dotfiles applied before the workspace accepts work.
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
const workspace = await sealant.workspaces.create({
|
|
133
|
+
repository: "github.com/acme/billing-service",
|
|
134
|
+
harness: codex(),
|
|
135
|
+
shell: "zsh",
|
|
136
|
+
dotfiles: {
|
|
137
|
+
// A repo the platform clones and applies (chezmoi/stow layouts detected, else copied):
|
|
138
|
+
repository: { url: "github.com/acme/dotfiles" },
|
|
139
|
+
// And/or caller-resolved archives — a checkout cloned host-side with your own ssh identity,
|
|
140
|
+
// or a scanned selection of home files, sent as gzipped tars (max 4, ~4MB decoded each):
|
|
141
|
+
archives: [{ data: tarGzBase64, manager: "copy", bootstrap: false }],
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`shell` installs the shell package and switches the login shell, so `.zshrc`/`.fishrc` actually take
|
|
147
|
+
effect. The `repository` applies first, then each archive in order, so local selections override
|
|
148
|
+
repo files. A failing apply fails the launch loudly rather than handing the agent a half-prepared
|
|
149
|
+
home. Managed OS families only — with `baseImage`, `dotfiles` and non-bash `shell` are rejected
|
|
150
|
+
client-side with the platform's reasoning.
|
|
151
|
+
|
|
152
|
+
## Custom base images
|
|
153
|
+
|
|
154
|
+
Instead of a managed OS family, a workspace image can be built from any image reference you already
|
|
155
|
+
trust:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
const workspace = await sealant.workspaces.create({
|
|
159
|
+
repository: "github.com/acme/billing-service",
|
|
160
|
+
harness: codex(),
|
|
161
|
+
baseImage: "node:22-bookworm",
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Distro package installs are skipped entirely — the build overlays only the `sealantd` supervisor
|
|
166
|
+
(PID 1), the harness CLIs (installed with `npm`), and a fully static `socat` (the control-socket
|
|
167
|
+
relay), all copied in as static binaries. This is the **base-image contract**, checked at build time
|
|
168
|
+
with readable failures:
|
|
169
|
+
|
|
170
|
+
- **Any Linux base, `amd64`/`arm64`**, with a **POSIX shell** at `/bin/sh`. Shells beyond that are
|
|
171
|
+
not assumed: the workspace login shell is `/bin/sh`, and `defaultShell` selection is not supported
|
|
172
|
+
with `baseImage`.
|
|
173
|
+
- **Node.js + npm** at or above the harness CLIs' floor (the CLIs are installed with
|
|
174
|
+
`npm install -g` and run on the base's node).
|
|
175
|
+
- **git**, for clone- and mount-sourced workspaces.
|
|
176
|
+
|
|
177
|
+
`packages` still works: names pass through **verbatim** (no portable-name resolution) to the base's
|
|
178
|
+
own package manager — `apt`, `apk`, `dnf`, or `pacman`, autodetected — and the build fails with a
|
|
179
|
+
readable error when the base has none. Dotfiles are not supported with `baseImage`. `baseImage` and
|
|
180
|
+
`os` are mutually exclusive.
|
|
181
|
+
|
|
126
182
|
## Inference on connected accounts
|
|
127
183
|
|
|
128
184
|
Run short, tool-calling inference loops on the caller's own subscription — server-side, through the
|
|
@@ -294,7 +294,7 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
294
294
|
readonly resolvePackage: <Mode extends import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode = import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode>(request: {
|
|
295
295
|
readonly query: {
|
|
296
296
|
readonly query: string;
|
|
297
|
-
readonly targetOs?: "arch" | "fedora" | "nix" | undefined;
|
|
297
|
+
readonly targetOs?: "arch" | "fedora" | "nix" | "ubuntu" | undefined;
|
|
298
298
|
};
|
|
299
299
|
readonly responseMode?: Mode;
|
|
300
300
|
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
@@ -329,6 +329,14 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
329
329
|
readonly version?: string | undefined;
|
|
330
330
|
readonly status?: string | undefined;
|
|
331
331
|
};
|
|
332
|
+
readonly ubuntu: {
|
|
333
|
+
readonly supported: boolean;
|
|
334
|
+
readonly repo?: string | undefined;
|
|
335
|
+
readonly packageName?: string | undefined;
|
|
336
|
+
readonly projectName?: string | undefined;
|
|
337
|
+
readonly version?: string | undefined;
|
|
338
|
+
readonly status?: string | undefined;
|
|
339
|
+
};
|
|
332
340
|
};
|
|
333
341
|
readonly alternatives: readonly {
|
|
334
342
|
readonly projectName: string;
|
|
@@ -1604,7 +1612,7 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
1604
1612
|
readonly resolvePackage: <Mode extends import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode = import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode>(request: {
|
|
1605
1613
|
readonly query: {
|
|
1606
1614
|
readonly query: string;
|
|
1607
|
-
readonly targetOs?: "arch" | "fedora" | "nix" | undefined;
|
|
1615
|
+
readonly targetOs?: "arch" | "fedora" | "nix" | "ubuntu" | undefined;
|
|
1608
1616
|
};
|
|
1609
1617
|
readonly responseMode?: Mode;
|
|
1610
1618
|
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
@@ -1639,6 +1647,14 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
1639
1647
|
readonly version?: string | undefined;
|
|
1640
1648
|
readonly status?: string | undefined;
|
|
1641
1649
|
};
|
|
1650
|
+
readonly ubuntu: {
|
|
1651
|
+
readonly supported: boolean;
|
|
1652
|
+
readonly repo?: string | undefined;
|
|
1653
|
+
readonly packageName?: string | undefined;
|
|
1654
|
+
readonly projectName?: string | undefined;
|
|
1655
|
+
readonly version?: string | undefined;
|
|
1656
|
+
readonly status?: string | undefined;
|
|
1657
|
+
};
|
|
1642
1658
|
};
|
|
1643
1659
|
readonly alternatives: readonly {
|
|
1644
1660
|
readonly projectName: string;
|
|
@@ -38,6 +38,20 @@ export const buildCreateWorkspaceRequest = (options, config) => {
|
|
|
38
38
|
code: "invalid_create_options",
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
+
if (options.os !== undefined && options.baseImage !== undefined) {
|
|
42
|
+
throw new SealantError("workspaces.create accepts either `os` (a managed OS family) or `baseImage` (a custom base image reference), not both.", { code: "invalid_create_options" });
|
|
43
|
+
}
|
|
44
|
+
if (options.baseImage !== undefined && options.dotfiles !== undefined) {
|
|
45
|
+
throw new SealantError("`dotfiles` is not supported with `baseImage`: custom bases guarantee only a POSIX shell, so the dotfiles managers are not provisioned.", { code: "invalid_create_options" });
|
|
46
|
+
}
|
|
47
|
+
if (options.baseImage !== undefined && options.shell !== undefined && options.shell !== "bash") {
|
|
48
|
+
throw new SealantError("`shell` is not supported with `baseImage`: custom bases run /bin/sh and the login shell cannot be switched.", { code: "invalid_create_options" });
|
|
49
|
+
}
|
|
50
|
+
if (options.dotfiles !== undefined &&
|
|
51
|
+
options.dotfiles.repository === undefined &&
|
|
52
|
+
(options.dotfiles.archives === undefined || options.dotfiles.archives.length === 0)) {
|
|
53
|
+
throw new SealantError("`dotfiles` requires a `repository`, at least one entry in `archives`, or both.", { code: "invalid_create_options" });
|
|
54
|
+
}
|
|
41
55
|
const sourceName = options.repository ?? options.source?.path ?? "workspace";
|
|
42
56
|
const tail = sourceName
|
|
43
57
|
.split("/")
|
|
@@ -69,6 +83,8 @@ export const buildCreateWorkspaceRequest = (options, config) => {
|
|
|
69
83
|
...(toolingPackages.length === 0 ? {} : { packages: toolingPackages }),
|
|
70
84
|
...(dockerService ? { services: { docker: { enabled: true } } } : {}),
|
|
71
85
|
};
|
|
86
|
+
const dotfilesRepository = options.dotfiles?.repository;
|
|
87
|
+
const dotfilesArchives = options.dotfiles?.archives ?? [];
|
|
72
88
|
const spec = {
|
|
73
89
|
version: "1",
|
|
74
90
|
sources: {
|
|
@@ -81,6 +97,21 @@ export const buildCreateWorkspaceRequest = (options, config) => {
|
|
|
81
97
|
...(options.ref === undefined ? {} : { ref: options.ref }),
|
|
82
98
|
}
|
|
83
99
|
: { kind: "mount", hostPath: options.source?.path },
|
|
100
|
+
...(dotfilesRepository === undefined
|
|
101
|
+
? {}
|
|
102
|
+
: {
|
|
103
|
+
inputs: [
|
|
104
|
+
{
|
|
105
|
+
id: "dotfiles",
|
|
106
|
+
kind: "git",
|
|
107
|
+
purpose: "dotfiles",
|
|
108
|
+
provider: "generic",
|
|
109
|
+
url: toGitUrl(dotfilesRepository.url),
|
|
110
|
+
// Omitted ref = the remote's default branch — never assumed to be `main`.
|
|
111
|
+
...(dotfilesRepository.ref === undefined ? {} : { ref: dotfilesRepository.ref }),
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
}),
|
|
84
115
|
...(mounts.length === 0
|
|
85
116
|
? {}
|
|
86
117
|
: {
|
|
@@ -93,9 +124,39 @@ export const buildCreateWorkspaceRequest = (options, config) => {
|
|
|
93
124
|
}),
|
|
94
125
|
},
|
|
95
126
|
harness: { id: options.harness.id },
|
|
96
|
-
customization: {
|
|
127
|
+
customization: {
|
|
128
|
+
enableSealantd: true,
|
|
129
|
+
...(options.shell === undefined ? {} : { defaultShell: options.shell }),
|
|
130
|
+
// The repository path's knobs live at customization level; archives carry their own.
|
|
131
|
+
...(dotfilesRepository?.manager === undefined
|
|
132
|
+
? {}
|
|
133
|
+
: { dotfilesManager: dotfilesRepository.manager }),
|
|
134
|
+
...(dotfilesRepository?.bootstrap === undefined
|
|
135
|
+
? {}
|
|
136
|
+
: { dotfilesBootstrap: dotfilesRepository.bootstrap }),
|
|
137
|
+
...(dotfilesRepository?.bootstrapCommand === undefined
|
|
138
|
+
? {}
|
|
139
|
+
: { dotfilesBootstrapCommand: dotfilesRepository.bootstrapCommand }),
|
|
140
|
+
},
|
|
141
|
+
...(dotfilesArchives.length === 0
|
|
142
|
+
? {}
|
|
143
|
+
: {
|
|
144
|
+
runtime: {
|
|
145
|
+
dotfilesArchives: dotfilesArchives.map((archive) => ({
|
|
146
|
+
data: archive.data,
|
|
147
|
+
...(archive.manager === undefined ? {} : { manager: archive.manager }),
|
|
148
|
+
...(archive.target === undefined ? {} : { target: archive.target }),
|
|
149
|
+
...(archive.bootstrap === undefined ? {} : { bootstrap: archive.bootstrap }),
|
|
150
|
+
...(archive.bootstrapCommand === undefined
|
|
151
|
+
? {}
|
|
152
|
+
: { bootstrapCommand: archive.bootstrapCommand }),
|
|
153
|
+
})),
|
|
154
|
+
},
|
|
155
|
+
}),
|
|
97
156
|
target: {
|
|
98
|
-
os:
|
|
157
|
+
os: options.baseImage !== undefined
|
|
158
|
+
? { family: "custom", mode: "require", baseImage: options.baseImage }
|
|
159
|
+
: { family: options.os ?? "fedora", mode: "prefer" },
|
|
99
160
|
runtime: { family: "docker", mode: "require" },
|
|
100
161
|
},
|
|
101
162
|
lifecycle: {
|
package/dist/types.d.ts
CHANGED
|
@@ -65,7 +65,8 @@ export interface WorkspaceEvent {
|
|
|
65
65
|
readonly message?: string;
|
|
66
66
|
}
|
|
67
67
|
/** The supported workspace OS families (maps to the blueprint target). */
|
|
68
|
-
|
|
68
|
+
/** Supported workspace image OS families. `fedora` is the default when `os` is omitted. */
|
|
69
|
+
export type WorkspaceOs = "fedora" | "arch" | "nix" | "ubuntu";
|
|
69
70
|
/**
|
|
70
71
|
* Connected-account credentials to attach to a workspace at creation time, per provider — so the
|
|
71
72
|
* harness inside the workspace authenticates as the caller's own Claude / Codex / GitHub identity
|
|
@@ -122,6 +123,53 @@ export interface WorkspaceExtraMount {
|
|
|
122
123
|
/** Defaults to `true`. Pass `false` deliberately — writes to extra mounts are unrecorded. */
|
|
123
124
|
readonly readOnly?: boolean;
|
|
124
125
|
}
|
|
126
|
+
/** How a dotfiles tree is applied inside the workspace. */
|
|
127
|
+
export type WorkspaceDotfilesManager = "auto" | "chezmoi" | "stow" | "copy";
|
|
128
|
+
/**
|
|
129
|
+
* A dotfiles repository the platform clones and applies before the workspace accepts work. Public
|
|
130
|
+
* or GitHub-App-reachable repos work as-is; for a repo only the caller's own ssh identity can
|
|
131
|
+
* reach, resolve it host-side and send the checkout as an archive instead (see
|
|
132
|
+
* `WorkspaceDotfilesArchive`).
|
|
133
|
+
*/
|
|
134
|
+
export interface WorkspaceDotfilesRepository {
|
|
135
|
+
/** Clone URL (or `"github.com/acme/dotfiles"` shorthand). */
|
|
136
|
+
readonly url: string;
|
|
137
|
+
/** Git ref. Omitted = the remote's default branch — never assumed to be `main`. */
|
|
138
|
+
readonly ref?: string;
|
|
139
|
+
/** Defaults to `"auto"`: chezmoi/stow layouts are detected, everything else is copied. */
|
|
140
|
+
readonly manager?: WorkspaceDotfilesManager;
|
|
141
|
+
/** Run the repo's bootstrap command after applying (skipped when absent). Defaults to true. */
|
|
142
|
+
readonly bootstrap?: boolean;
|
|
143
|
+
/** Bootstrap command, relative to the checkout. Defaults to `./install.sh`. */
|
|
144
|
+
readonly bootstrapCommand?: string;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* A caller-resolved dotfiles tree: a gzipped tar the daemon extracts and applies at boot through
|
|
148
|
+
* the same manager dispatch as a cloned repo. This is the shape for dotfiles resolved host-side —
|
|
149
|
+
* a checkout cloned with the caller's own ssh identity, or a scanned selection of home files —
|
|
150
|
+
* so no URL or credential ever has to reach the workspace. Max 4 archives, ~4MB decoded each.
|
|
151
|
+
*/
|
|
152
|
+
export interface WorkspaceDotfilesArchive {
|
|
153
|
+
/** base64 of a `.tar.gz` whose contents apply relative to the target. */
|
|
154
|
+
readonly data: string;
|
|
155
|
+
/** Defaults to `"auto"`. Scanned home files usually want `"copy"`. */
|
|
156
|
+
readonly manager?: WorkspaceDotfilesManager;
|
|
157
|
+
/** Where the tree lands: `"home"` (default) or `"config"` (`$HOME/.config`, copy manager only). */
|
|
158
|
+
readonly target?: "home" | "config";
|
|
159
|
+
/** Run `./install.sh` (or `bootstrapCommand`) after applying when present. Defaults to true. */
|
|
160
|
+
readonly bootstrap?: boolean;
|
|
161
|
+
readonly bootstrapCommand?: string;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Dotfiles for the workspace: a repository the platform clones, caller-resolved archives, or both
|
|
165
|
+
* (the repository applies first, archives after — in order — so local selections override repo
|
|
166
|
+
* files). Applied before the workspace reports ready; a failing apply fails the launch loudly.
|
|
167
|
+
* Not supported with `baseImage` (custom bases guarantee only a POSIX shell).
|
|
168
|
+
*/
|
|
169
|
+
export interface WorkspaceDotfilesOptions {
|
|
170
|
+
readonly repository?: WorkspaceDotfilesRepository;
|
|
171
|
+
readonly archives?: readonly WorkspaceDotfilesArchive[];
|
|
172
|
+
}
|
|
125
173
|
/** Runtime-managed services attached only to this workspace. */
|
|
126
174
|
export interface WorkspaceServicesOptions {
|
|
127
175
|
/**
|
|
@@ -146,10 +194,27 @@ export interface CreateOptions {
|
|
|
146
194
|
readonly ref?: string;
|
|
147
195
|
/** Human-friendly name for the workspace. */
|
|
148
196
|
readonly name?: string;
|
|
149
|
-
/** OS family for the workspace image. */
|
|
197
|
+
/** OS family for the workspace image. Mutually exclusive with `baseImage`. */
|
|
150
198
|
readonly os?: WorkspaceOs;
|
|
199
|
+
/**
|
|
200
|
+
* Build the workspace image FROM this arbitrary OCI image reference instead of a managed OS
|
|
201
|
+
* family (e.g. `"node:22-bookworm"`). Distro package installs are skipped; the build overlays
|
|
202
|
+
* only the sealantd supervisor, the harness CLIs (npm), and a static socat relay. See "Custom
|
|
203
|
+
* base images" in the SDK README for the base-image contract. Mutually exclusive with `os`;
|
|
204
|
+
* `packages` install through the base's own package manager (apt/apk/dnf/pacman) and fail the
|
|
205
|
+
* build readable when it has none.
|
|
206
|
+
*/
|
|
207
|
+
readonly baseImage?: string;
|
|
151
208
|
/** Extra OS packages to install in the workspace. */
|
|
152
209
|
readonly packages?: readonly string[];
|
|
210
|
+
/**
|
|
211
|
+
* Login shell for the workspace user (`"bash"` default). The shell package is installed and the
|
|
212
|
+
* login shell switched, so dotfiles like `.zshrc` actually take effect. Managed OS families
|
|
213
|
+
* only — custom bases guarantee just a POSIX shell.
|
|
214
|
+
*/
|
|
215
|
+
readonly shell?: "bash" | "zsh" | "fish";
|
|
216
|
+
/** Dotfiles applied before the workspace accepts work (see `WorkspaceDotfilesOptions`). */
|
|
217
|
+
readonly dotfiles?: WorkspaceDotfilesOptions;
|
|
153
218
|
/** Runtime-managed services that need more than installing an OS package. */
|
|
154
219
|
readonly services?: WorkspaceServicesOptions;
|
|
155
220
|
/** When true (default), resolve only once the workspace runtime is live. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sealant/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "The fluent public SDK for Sealant — create a workspace, run a harness, replay the record.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"access": "public"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@sealant/api-contracts": "^0.
|
|
29
|
+
"@sealant/api-contracts": "^0.18.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@effect/vitest": "4.0.0-beta.85",
|