@sealant/sdk 0.3.1 → 0.5.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 +107 -12
- package/dist/client.d.ts +12 -7
- package/dist/client.js +33 -21
- package/dist/effect/api-client.d.ts +408 -250
- package/dist/effect/exec-workspace.d.ts +5 -0
- package/dist/effect/exec-workspace.js +60 -0
- package/dist/effect/index.d.ts +27 -0
- package/dist/effect/index.js +31 -0
- package/dist/effect/operations.d.ts +90 -22
- package/dist/effect/operations.js +7 -4
- package/dist/effect/run-harness.d.ts +2 -2
- package/dist/effect/run-harness.js +4 -4
- package/dist/errors.d.ts +1 -1
- package/dist/errors.js +1 -1
- package/dist/facade/record.d.ts +17 -1
- package/dist/facade/record.js +17 -3
- package/dist/facade/run.d.ts +1 -1
- package/dist/facade/{sandbox.d.ts → workspace.d.ts} +6 -6
- package/dist/facade/{sandbox.js → workspace.js} +19 -17
- package/dist/harness.d.ts +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/internal/blueprint.d.ts +1 -1
- package/dist/internal/blueprint.js +8 -8
- package/dist/internal/config.d.ts +2 -2
- package/dist/internal/credentials.d.ts +7 -7
- package/dist/internal/credentials.js +2 -2
- package/dist/internal/inference.d.ts +47 -0
- package/dist/internal/inference.js +78 -0
- package/dist/internal/map-error.d.ts +1 -1
- package/dist/internal/map-error.js +1 -1
- package/dist/types.d.ts +310 -39
- package/dist/types.js +2 -2
- package/package.json +7 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @sealant/sdk — the fluent public SDK for Sealant.
|
|
3
3
|
*
|
|
4
|
-
* Create a live
|
|
5
|
-
* while it works, and keep the replayable execution record after the
|
|
4
|
+
* Create a live workspace around a real repository, run the harness you already use, stream progress
|
|
5
|
+
* while it works, and keep the replayable execution record after the workspace is gone:
|
|
6
6
|
*
|
|
7
7
|
* import { Sealant, opencode } from "@sealant/sdk"
|
|
8
8
|
*
|
|
9
9
|
* const sealant = new Sealant({ baseUrl: "http://localhost:8080" })
|
|
10
|
-
* const
|
|
10
|
+
* const workspace = await sealant.workspaces.create({
|
|
11
11
|
* repository: "github.com/acme/billing-service",
|
|
12
12
|
* harness: opencode(),
|
|
13
13
|
* })
|
|
14
|
-
* const run = await
|
|
14
|
+
* const run = await workspace.harness.run("Round invoice totals once, after applying the discount.")
|
|
15
15
|
* await run.record.replay()
|
|
16
16
|
*/
|
|
17
17
|
export { Sealant } from "./client.js";
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @sealant/sdk — the fluent public SDK for Sealant.
|
|
3
3
|
*
|
|
4
|
-
* Create a live
|
|
5
|
-
* while it works, and keep the replayable execution record after the
|
|
4
|
+
* Create a live workspace around a real repository, run the harness you already use, stream progress
|
|
5
|
+
* while it works, and keep the replayable execution record after the workspace is gone:
|
|
6
6
|
*
|
|
7
7
|
* import { Sealant, opencode } from "@sealant/sdk"
|
|
8
8
|
*
|
|
9
9
|
* const sealant = new Sealant({ baseUrl: "http://localhost:8080" })
|
|
10
|
-
* const
|
|
10
|
+
* const workspace = await sealant.workspaces.create({
|
|
11
11
|
* repository: "github.com/acme/billing-service",
|
|
12
12
|
* harness: opencode(),
|
|
13
13
|
* })
|
|
14
|
-
* const run = await
|
|
14
|
+
* const run = await workspace.harness.run("Round invoice totals once, after applying the discount.")
|
|
15
15
|
* await run.record.replay()
|
|
16
16
|
*/
|
|
17
17
|
export { Sealant } from "./client.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CreateOptions } from "../types.js";
|
|
2
2
|
import type { SealantInternalConfig } from "./config.js";
|
|
3
|
-
export declare const
|
|
3
|
+
export declare const buildCreateWorkspaceRequest: (options: CreateOptions, config: SealantInternalConfig) => {
|
|
4
4
|
readonly payload: {
|
|
5
5
|
readonly ownerUserId: string;
|
|
6
6
|
readonly registryId: string;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Lowers the fluent `create({ repository, harness })` options into the existing
|
|
3
|
-
* `
|
|
3
|
+
* `createWorkspaceRequestSchema` the control plane accepts — entirely client-side, so the slice needs
|
|
4
4
|
* no contract change. The public `repository` is the SOURCE git repo (it becomes
|
|
5
|
-
* `spec.sources.
|
|
5
|
+
* `spec.sources.workspace.url`); the contract's `repository`/`tag` are the OCI push coordinates, which
|
|
6
6
|
* we derive. `customization.enableSealantd` is forced on (it bakes + launches the daemon the run path
|
|
7
7
|
* connects to), the runtime target is pinned to docker (the only bridgeable adapter today), and the
|
|
8
|
-
* foreground is a keepalive so the
|
|
8
|
+
* foreground is a keepalive so the workspace idles with the daemon up and the harness is exec'd on
|
|
9
9
|
* demand by `run()` rather than launched at boot. `options.credentials`, if present, is lowered via
|
|
10
|
-
* `
|
|
10
|
+
* `mapWorkspaceCredentials` (see `./credentials.js`) and folded into `spec.credentials`; the control
|
|
11
11
|
* plane resolves those account references server-side (never secret material over this path).
|
|
12
12
|
*/
|
|
13
13
|
import { randomUUID } from "node:crypto";
|
|
14
|
-
import {
|
|
14
|
+
import { mapWorkspaceCredentials } from "./credentials.js";
|
|
15
15
|
const sanitizeRepoSlug = (value) => {
|
|
16
16
|
const slug = value
|
|
17
17
|
.toLowerCase()
|
|
@@ -26,16 +26,16 @@ const toGitUrl = (repository) => {
|
|
|
26
26
|
}
|
|
27
27
|
return `https://${repository}.git`;
|
|
28
28
|
};
|
|
29
|
-
export const
|
|
29
|
+
export const buildCreateWorkspaceRequest = (options, config) => {
|
|
30
30
|
const tail = options.repository
|
|
31
31
|
.split("/")
|
|
32
32
|
.filter((s) => s.length > 0)
|
|
33
33
|
.pop() ?? options.repository;
|
|
34
|
-
const credentials =
|
|
34
|
+
const credentials = mapWorkspaceCredentials(options.credentials);
|
|
35
35
|
const spec = {
|
|
36
36
|
version: "1",
|
|
37
37
|
sources: {
|
|
38
|
-
|
|
38
|
+
workspace: {
|
|
39
39
|
kind: "git",
|
|
40
40
|
provider: "generic",
|
|
41
41
|
url: toGitUrl(options.repository),
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { SealantConfig } from "../types.js";
|
|
11
11
|
export interface SealantHostLocalConfig {
|
|
12
|
-
/** Owner principal the control plane attributes
|
|
12
|
+
/** Owner principal the control plane attributes workspaces/runs to (pre-auth). */
|
|
13
13
|
readonly ownerUserId: string;
|
|
14
|
-
/** Registry the
|
|
14
|
+
/** Registry the workspace image is published to and launched from. */
|
|
15
15
|
readonly registryId: string;
|
|
16
16
|
}
|
|
17
17
|
export interface SealantInternalConfig {
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Lowers the public `
|
|
3
|
-
* shape. Pure and side-effect free so it is unit-testable on its own; `
|
|
2
|
+
* Lowers the public `WorkspaceCredentialsOptions` onto the control-plane's workspace-create payload
|
|
3
|
+
* shape. Pure and side-effect free so it is unit-testable on its own; `buildCreateWorkspaceRequest`
|
|
4
4
|
* calls it and folds the result into the request it builds.
|
|
5
5
|
*
|
|
6
6
|
* SECURITY: this only ever moves account **references** (booleans/names/ids) — never token values or
|
|
7
7
|
* other secret material. `true` resolves to the literal account name `"default"`; a string passes
|
|
8
8
|
* through as the named account; `profile` becomes `profileId`.
|
|
9
9
|
*/
|
|
10
|
-
import type {
|
|
11
|
-
/** The control-plane's
|
|
12
|
-
export interface
|
|
10
|
+
import type { WorkspaceCredentialsOptions } from "../types.js";
|
|
11
|
+
/** The control-plane's workspace-create payload shape for connected-account credentials. */
|
|
12
|
+
export interface WorkspaceCredentialsPayload {
|
|
13
13
|
readonly profileId?: string;
|
|
14
14
|
readonly claude?: string;
|
|
15
15
|
readonly codex?: string;
|
|
16
16
|
readonly github?: string;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
* Maps `
|
|
19
|
+
* Maps `WorkspaceCredentialsOptions` to the API payload shape, or `undefined` when no credentials were
|
|
20
20
|
* requested (omitted entirely, rather than serialized as an empty object).
|
|
21
21
|
*/
|
|
22
|
-
export declare const
|
|
22
|
+
export declare const mapWorkspaceCredentials: (options: WorkspaceCredentialsOptions | undefined) => WorkspaceCredentialsPayload | undefined;
|
|
@@ -6,10 +6,10 @@ const mapAccountRef = (value) => {
|
|
|
6
6
|
return value === true ? DEFAULT_ACCOUNT_NAME : value;
|
|
7
7
|
};
|
|
8
8
|
/**
|
|
9
|
-
* Maps `
|
|
9
|
+
* Maps `WorkspaceCredentialsOptions` to the API payload shape, or `undefined` when no credentials were
|
|
10
10
|
* requested (omitted entirely, rather than serialized as an empty object).
|
|
11
11
|
*/
|
|
12
|
-
export const
|
|
12
|
+
export const mapWorkspaceCredentials = (options) => {
|
|
13
13
|
if (options === undefined) {
|
|
14
14
|
return undefined;
|
|
15
15
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { InferenceContinueOptions, InferenceRespondOptions, InferenceResponse } from "../types.js";
|
|
2
|
+
export declare const buildInferenceRespondRequest: (options: InferenceContinueOptions | InferenceRespondOptions, ownerUserId: string) => {
|
|
3
|
+
readonly ownerUserId: string;
|
|
4
|
+
readonly credentials?: {
|
|
5
|
+
readonly profileId?: string | undefined;
|
|
6
|
+
readonly claude?: string | undefined;
|
|
7
|
+
readonly codex?: string | undefined;
|
|
8
|
+
} | undefined;
|
|
9
|
+
readonly prompt?: string | undefined;
|
|
10
|
+
readonly system?: string | undefined;
|
|
11
|
+
readonly model?: string | undefined;
|
|
12
|
+
readonly maxTurns?: number | undefined;
|
|
13
|
+
readonly tools?: readonly {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly description?: string | undefined;
|
|
16
|
+
readonly inputSchema: unknown;
|
|
17
|
+
}[] | undefined;
|
|
18
|
+
readonly responseFormat?: {
|
|
19
|
+
readonly type: "json";
|
|
20
|
+
readonly schema?: unknown;
|
|
21
|
+
} | undefined;
|
|
22
|
+
readonly sessionId?: string | undefined;
|
|
23
|
+
readonly toolResults?: readonly {
|
|
24
|
+
readonly toolCallId: string;
|
|
25
|
+
readonly content: string;
|
|
26
|
+
readonly isError?: boolean | undefined;
|
|
27
|
+
}[] | undefined;
|
|
28
|
+
};
|
|
29
|
+
export declare const mapInferenceResponse: (wire: {
|
|
30
|
+
readonly sessionId: string;
|
|
31
|
+
readonly turn: {
|
|
32
|
+
readonly type: "text";
|
|
33
|
+
readonly text: string;
|
|
34
|
+
readonly json?: unknown;
|
|
35
|
+
} | {
|
|
36
|
+
readonly type: "toolCalls";
|
|
37
|
+
readonly calls: readonly {
|
|
38
|
+
readonly toolCallId: string;
|
|
39
|
+
readonly name: string;
|
|
40
|
+
readonly input: unknown;
|
|
41
|
+
}[];
|
|
42
|
+
};
|
|
43
|
+
readonly usage?: {
|
|
44
|
+
readonly inputTokens: number;
|
|
45
|
+
readonly outputTokens: number;
|
|
46
|
+
} | undefined;
|
|
47
|
+
}) => InferenceResponse;
|
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
export const buildInferenceRespondRequest = (options, ownerUserId) => {
|
|
9
|
+
if ("sessionId" in options) {
|
|
10
|
+
return {
|
|
11
|
+
ownerUserId,
|
|
12
|
+
sessionId: options.sessionId,
|
|
13
|
+
toolResults: options.toolResults.map((result) => ({
|
|
14
|
+
toolCallId: result.toolCallId,
|
|
15
|
+
content: result.content,
|
|
16
|
+
...(result.isError === undefined ? {} : { isError: result.isError }),
|
|
17
|
+
})),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const claude = mapAccountRef(options.credentials.claude);
|
|
21
|
+
const codex = mapAccountRef(options.credentials.codex);
|
|
22
|
+
return {
|
|
23
|
+
ownerUserId,
|
|
24
|
+
credentials: {
|
|
25
|
+
...(options.credentials.profile === undefined
|
|
26
|
+
? {}
|
|
27
|
+
: { profileId: options.credentials.profile }),
|
|
28
|
+
...(claude === undefined ? {} : { claude }),
|
|
29
|
+
...(codex === undefined ? {} : { codex }),
|
|
30
|
+
},
|
|
31
|
+
prompt: options.prompt,
|
|
32
|
+
...(options.system === undefined ? {} : { system: options.system }),
|
|
33
|
+
...(options.model === undefined ? {} : { model: options.model }),
|
|
34
|
+
...(options.maxTurns === undefined ? {} : { maxTurns: options.maxTurns }),
|
|
35
|
+
...(options.tools === undefined
|
|
36
|
+
? {}
|
|
37
|
+
: {
|
|
38
|
+
tools: options.tools.map((tool) => ({
|
|
39
|
+
name: tool.name,
|
|
40
|
+
...(tool.description === undefined ? {} : { description: tool.description }),
|
|
41
|
+
inputSchema: tool.inputSchema,
|
|
42
|
+
})),
|
|
43
|
+
}),
|
|
44
|
+
...(options.responseFormat === undefined
|
|
45
|
+
? {}
|
|
46
|
+
: {
|
|
47
|
+
responseFormat: {
|
|
48
|
+
type: options.responseFormat.type,
|
|
49
|
+
...(options.responseFormat.schema === undefined
|
|
50
|
+
? {}
|
|
51
|
+
: { schema: options.responseFormat.schema }),
|
|
52
|
+
},
|
|
53
|
+
}),
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
export const mapInferenceResponse = (wire) => {
|
|
57
|
+
const turn = wire.turn.type === "text"
|
|
58
|
+
? {
|
|
59
|
+
type: "text",
|
|
60
|
+
text: wire.turn.text,
|
|
61
|
+
...(wire.turn.json === undefined ? {} : { json: wire.turn.json }),
|
|
62
|
+
}
|
|
63
|
+
: {
|
|
64
|
+
type: "toolCalls",
|
|
65
|
+
calls: wire.turn.calls.map((call) => ({
|
|
66
|
+
toolCallId: call.toolCallId,
|
|
67
|
+
name: call.name,
|
|
68
|
+
input: call.input,
|
|
69
|
+
})),
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
sessionId: wire.sessionId,
|
|
73
|
+
turn,
|
|
74
|
+
...(wire.usage === undefined
|
|
75
|
+
? {}
|
|
76
|
+
: { usage: { inputTokens: wire.usage.inputTokens, outputTokens: wire.usage.outputTokens } }),
|
|
77
|
+
};
|
|
78
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
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. `
|
|
3
|
+
* fails with `@sealant/api-contracts` tagged errors (e.g. `WorkspaceNotFoundError`), Effect HTTP-client
|
|
4
4
|
* errors, or schema decode errors; the runtime squashes the `Cause` to its failure value and hands it
|
|
5
5
|
* here. This is the single funnel that keeps Effect internals out of the public, Promise-based API.
|
|
6
6
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
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. `
|
|
3
|
+
* fails with `@sealant/api-contracts` tagged errors (e.g. `WorkspaceNotFoundError`), Effect HTTP-client
|
|
4
4
|
* errors, or schema decode errors; the runtime squashes the `Cause` to its failure value and hands it
|
|
5
5
|
* here. This is the single funnel that keeps Effect internals out of the public, Promise-based API.
|
|
6
6
|
*/
|