@sealant/sdk 0.0.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 +202 -0
- package/README.md +61 -0
- package/dist/client.d.ts +20 -0
- package/dist/client.js +107 -0
- package/dist/effect/api-client.d.ts +1762 -0
- package/dist/effect/api-client.js +29 -0
- package/dist/effect/operations.d.ts +236 -0
- package/dist/effect/operations.js +18 -0
- package/dist/effect/run-harness.d.ts +9 -0
- package/dist/effect/run-harness.js +63 -0
- package/dist/effect/runtime.d.ts +22 -0
- package/dist/effect/runtime.js +63 -0
- package/dist/errors.d.ts +39 -0
- package/dist/errors.js +47 -0
- package/dist/facade/context.d.ts +9 -0
- package/dist/facade/context.js +9 -0
- package/dist/facade/record.d.ts +20 -0
- package/dist/facade/record.js +222 -0
- package/dist/facade/run.d.ts +30 -0
- package/dist/facade/run.js +57 -0
- package/dist/facade/sandbox.d.ts +23 -0
- package/dist/facade/sandbox.js +89 -0
- package/dist/harness.d.ts +34 -0
- package/dist/harness.js +37 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +19 -0
- package/dist/internal/blueprint.d.ts +30 -0
- package/dist/internal/blueprint.js +69 -0
- package/dist/internal/config.d.ts +24 -0
- package/dist/internal/config.js +17 -0
- package/dist/internal/credentials.d.ts +22 -0
- package/dist/internal/credentials.js +26 -0
- package/dist/internal/map-error.d.ts +8 -0
- package/dist/internal/map-error.js +44 -0
- package/dist/types.d.ts +291 -0
- package/dist/types.js +17 -0
- package/package.json +41 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const DEFAULT_OWNER_USER_ID = "usr_local";
|
|
2
|
+
// Must match the control-plane's REGISTRY_NAME (defaults to "default"); the API 404s otherwise.
|
|
3
|
+
const DEFAULT_REGISTRY_ID = "default";
|
|
4
|
+
const env = (key) => {
|
|
5
|
+
const value = process.env[key];
|
|
6
|
+
return value === undefined || value.length === 0 ? undefined : value;
|
|
7
|
+
};
|
|
8
|
+
/** Resolves the public config plus host-local needs (from env, with docker-compose defaults). */
|
|
9
|
+
export const resolveInternalConfig = (config) => ({
|
|
10
|
+
baseUrl: config.baseUrl,
|
|
11
|
+
apiKey: config.apiKey,
|
|
12
|
+
fetch: config.fetch,
|
|
13
|
+
hostLocal: {
|
|
14
|
+
ownerUserId: env("SEALANT_OWNER_USER_ID") ?? DEFAULT_OWNER_USER_ID,
|
|
15
|
+
registryId: env("SEALANT_REGISTRY_ID") ?? DEFAULT_REGISTRY_ID,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lowers the public `SandboxCredentialsOptions` onto the control-plane's sandbox-create payload
|
|
3
|
+
* shape. Pure and side-effect free so it is unit-testable on its own; `buildCreateSandboxRequest`
|
|
4
|
+
* calls it and folds the result into the request it builds.
|
|
5
|
+
*
|
|
6
|
+
* SECURITY: this only ever moves account **references** (booleans/names/ids) — never token values or
|
|
7
|
+
* other secret material. `true` resolves to the literal account name `"default"`; a string passes
|
|
8
|
+
* through as the named account; `profile` becomes `profileId`.
|
|
9
|
+
*/
|
|
10
|
+
import type { SandboxCredentialsOptions } from "../types.js";
|
|
11
|
+
/** The control-plane's sandbox-create payload shape for connected-account credentials. */
|
|
12
|
+
export interface SandboxCredentialsPayload {
|
|
13
|
+
readonly profileId?: string;
|
|
14
|
+
readonly claude?: string;
|
|
15
|
+
readonly codex?: string;
|
|
16
|
+
readonly github?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Maps `SandboxCredentialsOptions` to the API payload shape, or `undefined` when no credentials were
|
|
20
|
+
* requested (omitted entirely, rather than serialized as an empty object).
|
|
21
|
+
*/
|
|
22
|
+
export declare const mapSandboxCredentials: (options: SandboxCredentialsOptions | undefined) => SandboxCredentialsPayload | undefined;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const DEFAULT_ACCOUNT_NAME = "default";
|
|
2
|
+
const mapAccountRef = (value) => {
|
|
3
|
+
if (value === undefined || value === false) {
|
|
4
|
+
return undefined;
|
|
5
|
+
}
|
|
6
|
+
return value === true ? DEFAULT_ACCOUNT_NAME : value;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Maps `SandboxCredentialsOptions` to the API payload shape, or `undefined` when no credentials were
|
|
10
|
+
* requested (omitted entirely, rather than serialized as an empty object).
|
|
11
|
+
*/
|
|
12
|
+
export const mapSandboxCredentials = (options) => {
|
|
13
|
+
if (options === undefined) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
const claude = mapAccountRef(options.claude);
|
|
17
|
+
const codex = mapAccountRef(options.codex);
|
|
18
|
+
const github = mapAccountRef(options.github);
|
|
19
|
+
const payload = {
|
|
20
|
+
...(options.profile === undefined ? {} : { profileId: options.profile }),
|
|
21
|
+
...(claude === undefined ? {} : { claude }),
|
|
22
|
+
...(codex === undefined ? {} : { codex }),
|
|
23
|
+
...(github === undefined ? {} : { github }),
|
|
24
|
+
};
|
|
25
|
+
return Object.keys(payload).length > 0 ? payload : undefined;
|
|
26
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps any failure crossing the Effect boundary onto a plain public `SealantError`. The Effect core
|
|
3
|
+
* fails with `@sealant/api-contracts` tagged errors (e.g. `SandboxNotFoundError`), Effect HTTP-client
|
|
4
|
+
* errors, or schema decode errors; the runtime squashes the `Cause` to its failure value and hands it
|
|
5
|
+
* here. This is the single funnel that keeps Effect internals out of the public, Promise-based API.
|
|
6
|
+
*/
|
|
7
|
+
import { SealantError } from "../errors.js";
|
|
8
|
+
export declare const toSealantError: (cause: unknown) => SealantError;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps any failure crossing the Effect boundary onto a plain public `SealantError`. The Effect core
|
|
3
|
+
* fails with `@sealant/api-contracts` tagged errors (e.g. `SandboxNotFoundError`), Effect HTTP-client
|
|
4
|
+
* errors, or schema decode errors; the runtime squashes the `Cause` to its failure value and hands it
|
|
5
|
+
* here. This is the single funnel that keeps Effect internals out of the public, Promise-based API.
|
|
6
|
+
*/
|
|
7
|
+
import { SealantApiError, SealantError } from "../errors.js";
|
|
8
|
+
const asRecord = (value) => typeof value === "object" && value !== null ? value : undefined;
|
|
9
|
+
const extractStatus = (record) => {
|
|
10
|
+
if (record === undefined) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
if (typeof record["status"] === "number") {
|
|
14
|
+
return record["status"];
|
|
15
|
+
}
|
|
16
|
+
const response = asRecord(record["response"]);
|
|
17
|
+
if (response !== undefined && typeof response["status"] === "number") {
|
|
18
|
+
return response["status"];
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
};
|
|
22
|
+
export const toSealantError = (cause) => {
|
|
23
|
+
if (cause instanceof SealantError) {
|
|
24
|
+
return cause;
|
|
25
|
+
}
|
|
26
|
+
const record = asRecord(cause);
|
|
27
|
+
const tag = record === undefined ? undefined : record["_tag"];
|
|
28
|
+
const message = record !== undefined && typeof record["message"] === "string"
|
|
29
|
+
? record["message"]
|
|
30
|
+
: cause instanceof Error
|
|
31
|
+
? cause.message
|
|
32
|
+
: typeof cause === "string"
|
|
33
|
+
? cause
|
|
34
|
+
: "Sealant operation failed.";
|
|
35
|
+
if (typeof tag === "string") {
|
|
36
|
+
const status = extractStatus(record);
|
|
37
|
+
return new SealantApiError(message, {
|
|
38
|
+
code: tag,
|
|
39
|
+
...(status === undefined ? {} : { status }),
|
|
40
|
+
cause,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
return new SealantError(message, { cause });
|
|
44
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Sealant SDK public type surface.
|
|
3
|
+
*
|
|
4
|
+
* This is the fluent object model the marketing site commits to verbatim:
|
|
5
|
+
*
|
|
6
|
+
* const sandbox = await sealant.sandboxes.create({ repository, harness: opencode() })
|
|
7
|
+
* const run = await sandbox.harness.run("Round invoice totals once, after applying the discount.")
|
|
8
|
+
* await run.record.replay()
|
|
9
|
+
*
|
|
10
|
+
* Design rule (load-bearing): these public types are HAND-WRITTEN and DECOUPLED from the Effect-core
|
|
11
|
+
* and `@sealant/telemetry` internal shapes. The facade maps internal data onto these types so the
|
|
12
|
+
* public surface stays stable across Effect-v4-beta churn and internal read-model changes. The whole
|
|
13
|
+
* surface is typed NOW — including operations not yet implemented in the current slice — so callers
|
|
14
|
+
* compile against a stable contract from day one (unimplemented paths reject with
|
|
15
|
+
* `SealantNotImplementedError` at runtime, see `./errors.js`).
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Public client configuration. Intentionally minimal: a base URL and an API key. Host-local
|
|
19
|
+
* concerns required by the current slice (owner identity, registry, direct database access) live in
|
|
20
|
+
* a separate internal config and never leak into this published type — see `./internal-config.ts`
|
|
21
|
+
* when the Effect core lands.
|
|
22
|
+
*/
|
|
23
|
+
export interface SealantConfig {
|
|
24
|
+
/** Base URL of the Sealant control-plane API (e.g. `http://localhost:8080`). */
|
|
25
|
+
readonly baseUrl: string;
|
|
26
|
+
/** Bearer token for authenticated deployments. Optional for a localhost demo with no auth. */
|
|
27
|
+
readonly apiKey?: string;
|
|
28
|
+
/** Override the `fetch` implementation (tests, custom agents, proxies). */
|
|
29
|
+
readonly fetch?: typeof fetch;
|
|
30
|
+
}
|
|
31
|
+
/** The harnesses with first-class integrations baked into the platform today. */
|
|
32
|
+
export type HarnessId = "opencode" | "codex" | "claude-code";
|
|
33
|
+
/** A single one-shot command to invoke a harness against a prompt inside the sandbox. */
|
|
34
|
+
export interface HarnessRunCommand {
|
|
35
|
+
/** The executable to run (e.g. `"opencode"`). */
|
|
36
|
+
readonly executable: string;
|
|
37
|
+
/** Arguments, including the prompt where the harness expects it. */
|
|
38
|
+
readonly args: readonly string[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A harness is a thin client value: an identity plus the knowledge of how to invoke it one-shot
|
|
42
|
+
* against a prompt. `opencode()`, `codex()`, `claudeCode()` and `customHarness()` (see `./harness.js`)
|
|
43
|
+
* produce these. Invoke-knowledge starts SDK-side as `buildRunCommand`; it migrates server-side into
|
|
44
|
+
* the platform's harness integration in a later phase so every surface shares one source of truth.
|
|
45
|
+
*/
|
|
46
|
+
export interface Harness {
|
|
47
|
+
/** Stable id. Built-in harnesses use a `HarnessId`; custom harnesses carry their own string. */
|
|
48
|
+
readonly id: string;
|
|
49
|
+
/** Builds the one-shot invocation for a prompt. */
|
|
50
|
+
readonly buildRunCommand: (prompt: string) => HarnessRunCommand;
|
|
51
|
+
/** Optional install hints for custom harnesses (built-ins are resolved by the platform). */
|
|
52
|
+
readonly install?: {
|
|
53
|
+
readonly packages?: readonly string[];
|
|
54
|
+
readonly command?: string;
|
|
55
|
+
};
|
|
56
|
+
/** Optional launch command for an interactive session (defaults to the executable). */
|
|
57
|
+
readonly launchCommand?: string;
|
|
58
|
+
}
|
|
59
|
+
/** Lifecycle status of a sandbox. `stopped`/`expired` arrive with lifecycle close-out (Phase 3). */
|
|
60
|
+
export type SandboxStatus = "queued" | "running" | "ready" | "failed" | "cancelled";
|
|
61
|
+
/** A coarse lifecycle event observed while a sandbox is being provisioned. */
|
|
62
|
+
export interface SandboxEvent {
|
|
63
|
+
readonly type: string;
|
|
64
|
+
readonly occurredAt: string;
|
|
65
|
+
readonly message?: string;
|
|
66
|
+
}
|
|
67
|
+
/** The supported sandbox OS families (maps to the blueprint target). */
|
|
68
|
+
export type SandboxOs = "fedora" | "arch" | "nix";
|
|
69
|
+
/**
|
|
70
|
+
* Connected-account credentials to attach to a sandbox at creation time, per provider — so the
|
|
71
|
+
* harness inside the sandbox authenticates as the caller's own Claude / Codex / GitHub identity
|
|
72
|
+
* instead of running unauthenticated.
|
|
73
|
+
*
|
|
74
|
+
* For each provider: `true` means "my default account" (the one named `"default"`), and a `string`
|
|
75
|
+
* names a specific connected account. `profile` names a profile slug/id whose bundled per-provider
|
|
76
|
+
* bindings apply first; any explicit `claude`/`codex`/`github` field wins over the profile's binding
|
|
77
|
+
* for that provider.
|
|
78
|
+
*
|
|
79
|
+
* SECURITY: only account **references** (booleans/names/ids) ever cross this surface — token values,
|
|
80
|
+
* `auth.json` contents, and any other secret material never do. The control plane resolves references
|
|
81
|
+
* to encrypted credentials server-side and injects them at launch.
|
|
82
|
+
*/
|
|
83
|
+
export interface SandboxCredentialsOptions {
|
|
84
|
+
/** Profile id whose per-provider account bindings apply first. */
|
|
85
|
+
readonly profile?: string;
|
|
86
|
+
/** `true` for the caller's default Claude account, or a string naming a specific one. */
|
|
87
|
+
readonly claude?: boolean | string;
|
|
88
|
+
/** `true` for the caller's default Codex account, or a string naming a specific one. */
|
|
89
|
+
readonly codex?: boolean | string;
|
|
90
|
+
/** `true` for the caller's default GitHub account, or a string naming a specific one. */
|
|
91
|
+
readonly github?: boolean | string;
|
|
92
|
+
}
|
|
93
|
+
export interface CreateOptions {
|
|
94
|
+
/** Source git repository to build the sandbox around (e.g. `"github.com/acme/billing-service"`). */
|
|
95
|
+
readonly repository: string;
|
|
96
|
+
/** The harness to run inside the sandbox. */
|
|
97
|
+
readonly harness: Harness;
|
|
98
|
+
/** Git ref to check out (defaults to the repository's default branch). */
|
|
99
|
+
readonly ref?: string;
|
|
100
|
+
/** Human-friendly name for the sandbox. */
|
|
101
|
+
readonly name?: string;
|
|
102
|
+
/** OS family for the sandbox image. */
|
|
103
|
+
readonly os?: SandboxOs;
|
|
104
|
+
/** Extra OS packages to install in the sandbox. */
|
|
105
|
+
readonly packages?: readonly string[];
|
|
106
|
+
/** When true (default), resolve only once the sandbox runtime is live. */
|
|
107
|
+
readonly wait?: boolean;
|
|
108
|
+
/** Observe provisioning events as they happen. */
|
|
109
|
+
readonly onEvent?: (event: SandboxEvent) => void;
|
|
110
|
+
/** Connected-account credentials to attach to the sandbox (see `SandboxCredentialsOptions`). */
|
|
111
|
+
readonly credentials?: SandboxCredentialsOptions;
|
|
112
|
+
}
|
|
113
|
+
export interface ListOptions {
|
|
114
|
+
readonly status?: SandboxStatus;
|
|
115
|
+
readonly limit?: number;
|
|
116
|
+
}
|
|
117
|
+
/** A live, disposable development environment around a real repository. */
|
|
118
|
+
export interface Sandbox {
|
|
119
|
+
readonly id: string;
|
|
120
|
+
readonly name: string;
|
|
121
|
+
/** Current lifecycle status. */
|
|
122
|
+
status(): Promise<SandboxStatus>;
|
|
123
|
+
/** Resolves once the sandbox runtime is live and ready to accept a run. */
|
|
124
|
+
ready(): Promise<this>;
|
|
125
|
+
/** Run a harness in this sandbox. */
|
|
126
|
+
readonly harness: HarnessRunner;
|
|
127
|
+
/** Lifecycle events as an async stream. */
|
|
128
|
+
events(): AsyncIterable<SandboxEvent>;
|
|
129
|
+
/** Stop the sandbox now (Phase 3). */
|
|
130
|
+
stop(): Promise<void>;
|
|
131
|
+
/** Restart the sandbox into a fresh runtime (Phase 3). */
|
|
132
|
+
restart(): Promise<Sandbox>;
|
|
133
|
+
/** Schedule the sandbox to expire (Phase 3). */
|
|
134
|
+
expire(options?: {
|
|
135
|
+
readonly in?: string;
|
|
136
|
+
}): Promise<void>;
|
|
137
|
+
}
|
|
138
|
+
export interface RunOptions {
|
|
139
|
+
/** Cancel the run by aborting this signal. */
|
|
140
|
+
readonly signal?: AbortSignal;
|
|
141
|
+
/** Idempotency key so a retried call does not start a duplicate run. */
|
|
142
|
+
readonly idempotencyKey?: string;
|
|
143
|
+
}
|
|
144
|
+
export interface SessionOptions {
|
|
145
|
+
readonly signal?: AbortSignal;
|
|
146
|
+
}
|
|
147
|
+
/** Runs a harness in a sandbox, one-shot or interactive. */
|
|
148
|
+
export interface HarnessRunner {
|
|
149
|
+
/** BLOCKING: resolves once the harness has terminally completed; `result`/`changes` are settled. */
|
|
150
|
+
run(prompt: string, options?: RunOptions): Promise<Run>;
|
|
151
|
+
/** NON-BLOCKING: returns a live handle immediately for streaming via `run.record.stream()`. */
|
|
152
|
+
start(prompt: string, options?: RunOptions): Promise<Run>;
|
|
153
|
+
/** Interactive session reusing the live sandbox (Phase 3). */
|
|
154
|
+
session(options?: SessionOptions): Promise<InteractiveSession>;
|
|
155
|
+
}
|
|
156
|
+
export type RunOutcome = "completed" | "failed";
|
|
157
|
+
/** Lifecycle status of a run (harness execution). */
|
|
158
|
+
export type RunStatus = "queued" | "running" | "completed" | "failed" | "cancelled";
|
|
159
|
+
export interface RunResult {
|
|
160
|
+
/** Raw lifecycle status (honest for non-terminal runs read via `runs.get`). */
|
|
161
|
+
readonly status: RunStatus;
|
|
162
|
+
/** Coarse terminal outcome: `completed` only when the run completed; otherwise `failed`. */
|
|
163
|
+
readonly outcome: RunOutcome;
|
|
164
|
+
readonly exitCode: number;
|
|
165
|
+
readonly summary?: string;
|
|
166
|
+
}
|
|
167
|
+
export type FileChangeKind = "added" | "modified" | "deleted" | "renamed";
|
|
168
|
+
export interface RunFileChange {
|
|
169
|
+
readonly path: string;
|
|
170
|
+
readonly change: FileChangeKind;
|
|
171
|
+
/** Previous path for a rename. */
|
|
172
|
+
readonly oldPath?: string;
|
|
173
|
+
}
|
|
174
|
+
export interface RunChanges {
|
|
175
|
+
readonly files: readonly RunFileChange[];
|
|
176
|
+
/** The unified diff of everything that changed. */
|
|
177
|
+
diff(): Promise<string>;
|
|
178
|
+
}
|
|
179
|
+
export interface ArtifactRef {
|
|
180
|
+
readonly name: string;
|
|
181
|
+
readonly bytes: number;
|
|
182
|
+
readonly contentType?: string;
|
|
183
|
+
}
|
|
184
|
+
export interface RunArtifacts {
|
|
185
|
+
list(): Promise<readonly ArtifactRef[]>;
|
|
186
|
+
get(name: string): Promise<Uint8Array>;
|
|
187
|
+
}
|
|
188
|
+
/** A single ordered entry in the execution record's timeline. */
|
|
189
|
+
export interface TimelineEntry {
|
|
190
|
+
readonly sequence: bigint;
|
|
191
|
+
readonly kind: string;
|
|
192
|
+
readonly occurredAt: string;
|
|
193
|
+
readonly data: unknown;
|
|
194
|
+
}
|
|
195
|
+
/** A re-fold of the record up to some point — scrubable by sequence. */
|
|
196
|
+
export interface RunReplay {
|
|
197
|
+
readonly entries: readonly TimelineEntry[];
|
|
198
|
+
/** The entry at (or the last entry at-or-before) `sequence`. */
|
|
199
|
+
at(sequence: bigint): TimelineEntry | undefined;
|
|
200
|
+
}
|
|
201
|
+
/** One terminal command the run executed, reconstructed from the record (not raw event noise). */
|
|
202
|
+
export interface RunCommand {
|
|
203
|
+
/** The executable that ran (e.g. `"opencode"`). */
|
|
204
|
+
readonly executable: string;
|
|
205
|
+
/** Its arguments. */
|
|
206
|
+
readonly args: readonly string[];
|
|
207
|
+
/** A ready-to-read shell line, e.g. `opencode run "fix the test"`. */
|
|
208
|
+
readonly command: string;
|
|
209
|
+
/** Working directory the command ran in. */
|
|
210
|
+
readonly cwd?: string;
|
|
211
|
+
/** Exit code, when the command exited normally. */
|
|
212
|
+
readonly exitCode?: number;
|
|
213
|
+
/** Signal number, when the command was terminated by a signal instead. */
|
|
214
|
+
readonly signal?: number;
|
|
215
|
+
/** Wall-clock duration in milliseconds, when known. */
|
|
216
|
+
readonly durationMs?: number;
|
|
217
|
+
/** Bytes the command wrote to stdout / stderr (full text is available via `scrollback`). */
|
|
218
|
+
readonly stdoutBytes: number;
|
|
219
|
+
readonly stderrBytes: number;
|
|
220
|
+
}
|
|
221
|
+
/** Provenance-honest report of any gaps detected in the recorded stream. */
|
|
222
|
+
export interface LossReport {
|
|
223
|
+
readonly complete: boolean;
|
|
224
|
+
readonly spans: readonly {
|
|
225
|
+
readonly fromSequence?: bigint;
|
|
226
|
+
readonly toSequence?: bigint;
|
|
227
|
+
}[];
|
|
228
|
+
}
|
|
229
|
+
export interface RunSummary {
|
|
230
|
+
readonly runId: string;
|
|
231
|
+
readonly outcome: RunOutcome;
|
|
232
|
+
readonly entries: number;
|
|
233
|
+
readonly durationMs?: number;
|
|
234
|
+
}
|
|
235
|
+
/** Output streams a process can write to. */
|
|
236
|
+
export type IoStream = "stdout" | "stderr";
|
|
237
|
+
/**
|
|
238
|
+
* The execution record for a run: the durable, replayable history. Backed by the telemetry read
|
|
239
|
+
* facade. `replay()`/`timeline()`/`scrollback()`/`stream()` are available in the current slice; the
|
|
240
|
+
* time-travel folds (`fileTreeAt`/`processTreeAt`) reject until their read models land.
|
|
241
|
+
*/
|
|
242
|
+
export interface RunRecord {
|
|
243
|
+
readonly runId: string;
|
|
244
|
+
/** Re-fold the full record into a scrubable replay (low-level: every timeline entry). */
|
|
245
|
+
replay(options?: {
|
|
246
|
+
readonly speed?: number;
|
|
247
|
+
readonly onEntry?: (entry: TimelineEntry) => void;
|
|
248
|
+
}): Promise<RunReplay>;
|
|
249
|
+
/** The terminal commands the run executed — what the harness actually did, reconstructed. */
|
|
250
|
+
commands(): Promise<readonly RunCommand[]>;
|
|
251
|
+
/** A human-readable transcript: the commands and their outcomes, nicely laid out (no event noise). */
|
|
252
|
+
transcript(): Promise<string>;
|
|
253
|
+
/** Subscribe to the live event stream while the run is in progress (poll-backed; SSE later). */
|
|
254
|
+
stream(options?: {
|
|
255
|
+
readonly from?: bigint;
|
|
256
|
+
}): AsyncIterable<TimelineEntry>;
|
|
257
|
+
/** Iterate the full timeline as structured data. */
|
|
258
|
+
timeline(options?: {
|
|
259
|
+
readonly from?: bigint;
|
|
260
|
+
}): AsyncIterable<TimelineEntry>;
|
|
261
|
+
/** Byte-exact scrollback for a process's output stream. */
|
|
262
|
+
scrollback(processId: string, stream: IoStream): AsyncIterable<Uint8Array>;
|
|
263
|
+
/** Provenance-honest loss report. */
|
|
264
|
+
loss(): Promise<LossReport>;
|
|
265
|
+
/** A compact summary of the run. */
|
|
266
|
+
summary(): Promise<RunSummary>;
|
|
267
|
+
/** File-tree snapshot at a point in time (Phase 1 — rejects until backed). */
|
|
268
|
+
fileTreeAt(sequence: bigint): Promise<unknown>;
|
|
269
|
+
/** Process-tree snapshot at a point in time (Phase 1 — rejects until backed). */
|
|
270
|
+
processTreeAt(sequence: bigint): Promise<unknown>;
|
|
271
|
+
}
|
|
272
|
+
/** One unit of developer work: what it produced and how it happened. */
|
|
273
|
+
export interface Run {
|
|
274
|
+
readonly id: string;
|
|
275
|
+
/** Terminal result (settled once `run()` resolves). */
|
|
276
|
+
readonly result: RunResult;
|
|
277
|
+
/** The before/after of what changed. */
|
|
278
|
+
readonly changes: RunChanges;
|
|
279
|
+
/** Retained artifacts. */
|
|
280
|
+
readonly artifacts: RunArtifacts;
|
|
281
|
+
/** The execution record. */
|
|
282
|
+
readonly record: RunRecord;
|
|
283
|
+
/** Resolves once the run has terminally completed (no-op if already settled). */
|
|
284
|
+
wait(): Promise<Run>;
|
|
285
|
+
}
|
|
286
|
+
/** An interactive harness session over the live sandbox (Phase 3). */
|
|
287
|
+
export interface InteractiveSession {
|
|
288
|
+
send(input: string): Promise<void>;
|
|
289
|
+
output(): AsyncIterable<Uint8Array>;
|
|
290
|
+
close(): Promise<void>;
|
|
291
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Sealant SDK public type surface.
|
|
3
|
+
*
|
|
4
|
+
* This is the fluent object model the marketing site commits to verbatim:
|
|
5
|
+
*
|
|
6
|
+
* const sandbox = await sealant.sandboxes.create({ repository, harness: opencode() })
|
|
7
|
+
* const run = await sandbox.harness.run("Round invoice totals once, after applying the discount.")
|
|
8
|
+
* await run.record.replay()
|
|
9
|
+
*
|
|
10
|
+
* Design rule (load-bearing): these public types are HAND-WRITTEN and DECOUPLED from the Effect-core
|
|
11
|
+
* and `@sealant/telemetry` internal shapes. The facade maps internal data onto these types so the
|
|
12
|
+
* public surface stays stable across Effect-v4-beta churn and internal read-model changes. The whole
|
|
13
|
+
* surface is typed NOW — including operations not yet implemented in the current slice — so callers
|
|
14
|
+
* compile against a stable contract from day one (unimplemented paths reject with
|
|
15
|
+
* `SealantNotImplementedError` at runtime, see `./errors.js`).
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sealant/sdk",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"description": "The fluent public SDK for Sealant — create a sandbox, run a harness, replay the record.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/sealant-sh/sealant.git",
|
|
9
|
+
"directory": "packages/sdk"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"effect": "^4.0.0-beta.85",
|
|
26
|
+
"@sealant/api-contracts": "^0.0.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@effect/vitest": "^4.0.0-beta.85",
|
|
30
|
+
"@sealant/typescript": "0.0.0"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsgo -p tsconfig.build.json",
|
|
37
|
+
"lint": "oxlint .",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"typecheck": "tsgo -p tsconfig.json --noEmit"
|
|
40
|
+
}
|
|
41
|
+
}
|