@sealant/sdk 0.21.0 → 0.23.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/dist/client.d.ts +5 -1
- package/dist/client.js +57 -15
- package/dist/effect/api-client.d.ts +88 -0
- package/dist/effect/exec-workspace.js +2 -2
- package/dist/effect/index.d.ts +1 -1
- package/dist/effect/index.js +1 -1
- package/dist/effect/operations.d.ts +75 -4
- package/dist/effect/operations.js +18 -4
- package/dist/effect/run-harness.js +3 -3
- package/dist/facade/record.js +20 -7
- package/dist/facade/run.js +3 -2
- package/dist/facade/session.js +8 -9
- package/dist/facade/workspace.js +9 -9
- package/dist/internal/blueprint.js +5 -2
- package/dist/internal/config.d.ts +3 -3
- package/dist/internal/config.js +1 -1
- package/dist/types.d.ts +73 -1
- package/package.json +2 -2
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccessTokensNamespace, CreateOptions, InferenceNamespace, ListOptions, Run, Workspace, SealantConfig } from "./types.js";
|
|
1
|
+
import type { AccessTokensNamespace, ConnectedAccountsNamespace, CreateOptions, InferenceNamespace, ListOptions, Run, Workspace, SealantConfig, UsersNamespace } from "./types.js";
|
|
2
2
|
export declare class Sealant {
|
|
3
3
|
#private;
|
|
4
4
|
constructor(config: SealantConfig);
|
|
@@ -21,6 +21,10 @@ export declare class Sealant {
|
|
|
21
21
|
* the session endpoints enforce exactly those scopes (a read-stream token can stream but is
|
|
22
22
|
* rejected for input and exec).
|
|
23
23
|
*/
|
|
24
|
+
/** Identity rows for products that own their own login (one client per user afterwards). */
|
|
25
|
+
readonly users: UsersNamespace;
|
|
26
|
+
/** This client's owner's Claude / Codex / GitHub accounts. Secrets go in; none come out. */
|
|
27
|
+
readonly connectedAccounts: ConnectedAccountsNamespace;
|
|
24
28
|
readonly accessTokens: AccessTokensNamespace;
|
|
25
29
|
/** Runs by id — so a record can be replayed long after its workspace is gone. */
|
|
26
30
|
readonly runs: {
|
package/dist/client.js
CHANGED
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* `Sealant` — the public client and the import users reach for.
|
|
3
|
-
*
|
|
4
|
-
* import { Sealant, opencode } from "@sealant/sdk"
|
|
5
|
-
* const sealant = new Sealant({ baseUrl: "http://localhost:8080" })
|
|
6
|
-
*
|
|
7
|
-
* This is the plain-Promise facade over the Effect core: an app `Layer` built once in the
|
|
8
|
-
* constructor providing the wire client derived from `@sealant/api-contracts`. Everything is a plain
|
|
9
|
-
* HTTP call to `baseUrl`. Operations whose endpoints have not landed yet stay TYPED against the
|
|
10
|
-
* stable surface and reject with `SealantNotImplementedError` so callers can compile and wire
|
|
11
|
-
* against the final shape today.
|
|
12
|
-
*/
|
|
13
|
-
import { createAccessTokenOp, createWorkspaceOp, getRunOp, getWorkspaceOp, inferenceRespondOp, listWorkspacesOp, } from "./effect/operations.js";
|
|
1
|
+
import { archiveConnectedAccountOp, createAccessTokenOp, createConnectedAccountOp, createWorkspaceOp, ensureUserOp, getRunOp, getUserOp, getWorkspaceOp, inferenceRespondOp, listConnectedAccountsOp, listWorkspacesOp, } from "./effect/operations.js";
|
|
14
2
|
import { runHarness, startHarness } from "./effect/run-harness.js";
|
|
15
3
|
import { makeSdkRuntime } from "./effect/runtime.js";
|
|
16
4
|
import { SealantError } from "./errors.js";
|
|
@@ -20,6 +8,19 @@ import { buildCreateWorkspaceRequest } from "./internal/blueprint.js";
|
|
|
20
8
|
import { resolveInternalConfig } from "./internal/config.js";
|
|
21
9
|
import { parseTtlSeconds } from "./internal/duration.js";
|
|
22
10
|
import { buildInferenceRespondRequest, mapInferenceResponse } from "./internal/inference.js";
|
|
11
|
+
const mapConnectedAccount = (wire) => ({
|
|
12
|
+
connectedAccountId: wire.connectedAccountId,
|
|
13
|
+
ownerUserId: wire.ownerUserId,
|
|
14
|
+
provider: wire.provider,
|
|
15
|
+
name: wire.name,
|
|
16
|
+
kind: wire.kind,
|
|
17
|
+
status: wire.status,
|
|
18
|
+
metadata: wire.metadata,
|
|
19
|
+
connectedAt: wire.connectedAt,
|
|
20
|
+
updatedAt: wire.updatedAt,
|
|
21
|
+
lastUsedAt: wire.lastUsedAt,
|
|
22
|
+
lastSyncedAt: wire.lastSyncedAt,
|
|
23
|
+
});
|
|
23
24
|
// Wire the run-execution implementations into the Workspace facade (the injection point exists to
|
|
24
25
|
// break the workspace <-> run-harness import cycle; the client is the composition root).
|
|
25
26
|
registerHarnessExecutors({ run: runHarness, start: startHarness });
|
|
@@ -72,7 +73,7 @@ export class Sealant {
|
|
|
72
73
|
return workspace.ready();
|
|
73
74
|
},
|
|
74
75
|
get: async (id) => {
|
|
75
|
-
const details = await this.#runtime.run(getWorkspaceOp(id));
|
|
76
|
+
const details = await this.#runtime.run(getWorkspaceOp(id, this.#ctx.config.hostLocal.ownerUserId));
|
|
76
77
|
return makeWorkspace(this.#ctx, {
|
|
77
78
|
id: details.workspaceId,
|
|
78
79
|
name: details.name,
|
|
@@ -109,6 +110,47 @@ export class Sealant {
|
|
|
109
110
|
* the session endpoints enforce exactly those scopes (a read-stream token can stream but is
|
|
110
111
|
* rejected for input and exec).
|
|
111
112
|
*/
|
|
113
|
+
/** Identity rows for products that own their own login (one client per user afterwards). */
|
|
114
|
+
users = {
|
|
115
|
+
ensure: async (options) => {
|
|
116
|
+
const wire = await this.#runtime.run(ensureUserOp({
|
|
117
|
+
email: options.email,
|
|
118
|
+
name: options.name,
|
|
119
|
+
...(options.userId === undefined ? {} : { userId: options.userId }),
|
|
120
|
+
}));
|
|
121
|
+
return {
|
|
122
|
+
userId: wire.userId,
|
|
123
|
+
email: wire.email,
|
|
124
|
+
name: wire.name,
|
|
125
|
+
createdAt: wire.createdAt,
|
|
126
|
+
created: wire.created,
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
get: async (userId) => {
|
|
130
|
+
const wire = await this.#runtime.run(getUserOp(userId));
|
|
131
|
+
return { userId: wire.userId, email: wire.email, name: wire.name, createdAt: wire.createdAt };
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
/** This client's owner's Claude / Codex / GitHub accounts. Secrets go in; none come out. */
|
|
135
|
+
connectedAccounts = {
|
|
136
|
+
list: async () => {
|
|
137
|
+
const wire = await this.#runtime.run(listConnectedAccountsOp(this.#ctx.config.hostLocal.ownerUserId));
|
|
138
|
+
return wire.items.map(mapConnectedAccount);
|
|
139
|
+
},
|
|
140
|
+
connect: async (options) => {
|
|
141
|
+
const wire = await this.#runtime.run(createConnectedAccountOp({
|
|
142
|
+
ownerUserId: this.#ctx.config.hostLocal.ownerUserId,
|
|
143
|
+
provider: options.provider,
|
|
144
|
+
secret: options.secret,
|
|
145
|
+
...(options.name === undefined ? {} : { name: options.name }),
|
|
146
|
+
}));
|
|
147
|
+
return mapConnectedAccount(wire);
|
|
148
|
+
},
|
|
149
|
+
disconnect: async (connectedAccountId) => {
|
|
150
|
+
const wire = await this.#runtime.run(archiveConnectedAccountOp(connectedAccountId, this.#ctx.config.hostLocal.ownerUserId));
|
|
151
|
+
return mapConnectedAccount(wire);
|
|
152
|
+
},
|
|
153
|
+
};
|
|
112
154
|
accessTokens = {
|
|
113
155
|
create: async (options) => {
|
|
114
156
|
const wire = await this.#runtime.run(createAccessTokenOp({
|
|
@@ -130,7 +172,7 @@ export class Sealant {
|
|
|
130
172
|
/** Runs by id — so a record can be replayed long after its workspace is gone. */
|
|
131
173
|
runs = {
|
|
132
174
|
get: async (runId) => {
|
|
133
|
-
const wire = await this.#runtime.run(getRunOp(runId));
|
|
175
|
+
const wire = await this.#runtime.run(getRunOp(runId, this.#ctx.config.hostLocal.ownerUserId));
|
|
134
176
|
return makeRun(this.#ctx, { wire });
|
|
135
177
|
},
|
|
136
178
|
};
|
|
@@ -537,6 +537,9 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
537
537
|
readonly params: {
|
|
538
538
|
readonly runId: string;
|
|
539
539
|
};
|
|
540
|
+
readonly query: {
|
|
541
|
+
readonly ownerUserId?: string | undefined;
|
|
542
|
+
};
|
|
540
543
|
readonly responseMode?: Mode;
|
|
541
544
|
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
542
545
|
readonly runId: string;
|
|
@@ -566,6 +569,9 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
566
569
|
readonly params: {
|
|
567
570
|
readonly runId: string;
|
|
568
571
|
};
|
|
572
|
+
readonly query: {
|
|
573
|
+
readonly ownerUserId?: string | undefined;
|
|
574
|
+
};
|
|
569
575
|
readonly responseMode?: Mode;
|
|
570
576
|
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
571
577
|
readonly files: readonly {
|
|
@@ -580,6 +586,9 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
580
586
|
readonly runId: string;
|
|
581
587
|
readonly sequence: string;
|
|
582
588
|
};
|
|
589
|
+
readonly query: {
|
|
590
|
+
readonly ownerUserId?: string | undefined;
|
|
591
|
+
};
|
|
583
592
|
readonly responseMode?: Mode;
|
|
584
593
|
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
585
594
|
readonly eventId: string;
|
|
@@ -603,6 +612,9 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
603
612
|
readonly params: {
|
|
604
613
|
readonly runId: string;
|
|
605
614
|
};
|
|
615
|
+
readonly query: {
|
|
616
|
+
readonly ownerUserId?: string | undefined;
|
|
617
|
+
};
|
|
606
618
|
readonly responseMode?: Mode;
|
|
607
619
|
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
608
620
|
readonly runId: string;
|
|
@@ -624,6 +636,7 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
624
636
|
readonly runId: string;
|
|
625
637
|
};
|
|
626
638
|
readonly query: {
|
|
639
|
+
readonly ownerUserId?: string | undefined;
|
|
627
640
|
readonly processId: string;
|
|
628
641
|
readonly stream: "pty" | "stderr" | "stdout";
|
|
629
642
|
readonly atSequence?: string | undefined;
|
|
@@ -642,6 +655,7 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
642
655
|
readonly runId: string;
|
|
643
656
|
};
|
|
644
657
|
readonly query: {
|
|
658
|
+
readonly ownerUserId?: string | undefined;
|
|
645
659
|
readonly fromSequence?: string | undefined;
|
|
646
660
|
readonly toSequence?: string | undefined;
|
|
647
661
|
readonly limit?: string | undefined;
|
|
@@ -1038,6 +1052,33 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
1038
1052
|
readonly status: "ok";
|
|
1039
1053
|
}, Mode>, import("effect/unstable/http/HttpClientError").HttpClientError | ([Mode] extends ["response-only"] ? never : import("effect/Schema").SchemaError), [Mode] extends ["response-only"] ? never : never>;
|
|
1040
1054
|
};
|
|
1055
|
+
readonly users: {
|
|
1056
|
+
readonly ensureUser: <Mode extends import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode = import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode>(request: {
|
|
1057
|
+
readonly payload: {
|
|
1058
|
+
readonly email: string;
|
|
1059
|
+
readonly name: string;
|
|
1060
|
+
readonly userId?: string | undefined;
|
|
1061
|
+
};
|
|
1062
|
+
readonly responseMode?: Mode;
|
|
1063
|
+
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
1064
|
+
readonly userId: string;
|
|
1065
|
+
readonly email: string;
|
|
1066
|
+
readonly name: string;
|
|
1067
|
+
readonly createdAt: string;
|
|
1068
|
+
readonly created: boolean;
|
|
1069
|
+
}, Mode>, import("effect/unstable/http/HttpClientError").HttpClientError | ([Mode] extends ["response-only"] ? never : import("effect/Schema").SchemaError | import("@sealant/api-contracts").UserBadRequestError | import("@sealant/api-contracts").UserInternalServerError), [Mode] extends ["response-only"] ? never : never>;
|
|
1070
|
+
readonly getUser: <Mode extends import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode = import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode>(request: {
|
|
1071
|
+
readonly params: {
|
|
1072
|
+
readonly userId: string;
|
|
1073
|
+
};
|
|
1074
|
+
readonly responseMode?: Mode;
|
|
1075
|
+
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
1076
|
+
readonly userId: string;
|
|
1077
|
+
readonly email: string;
|
|
1078
|
+
readonly name: string;
|
|
1079
|
+
readonly createdAt: string;
|
|
1080
|
+
}, Mode>, import("effect/unstable/http/HttpClientError").HttpClientError | ([Mode] extends ["response-only"] ? never : import("effect/Schema").SchemaError | import("@sealant/api-contracts").UserInternalServerError | import("@sealant/api-contracts").UserNotFoundError), [Mode] extends ["response-only"] ? never : never>;
|
|
1081
|
+
};
|
|
1041
1082
|
readonly workspaces: {
|
|
1042
1083
|
readonly createWorkspace: <Mode extends import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode = import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode>(request: {
|
|
1043
1084
|
readonly headers: {
|
|
@@ -1136,6 +1177,9 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
1136
1177
|
readonly params: {
|
|
1137
1178
|
readonly workspaceId: string;
|
|
1138
1179
|
};
|
|
1180
|
+
readonly query: {
|
|
1181
|
+
readonly ownerUserId?: string | undefined;
|
|
1182
|
+
};
|
|
1139
1183
|
readonly responseMode?: Mode;
|
|
1140
1184
|
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
1141
1185
|
readonly workspaceId: string;
|
|
@@ -1863,6 +1907,9 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
1863
1907
|
readonly params: {
|
|
1864
1908
|
readonly runId: string;
|
|
1865
1909
|
};
|
|
1910
|
+
readonly query: {
|
|
1911
|
+
readonly ownerUserId?: string | undefined;
|
|
1912
|
+
};
|
|
1866
1913
|
readonly responseMode?: Mode;
|
|
1867
1914
|
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
1868
1915
|
readonly runId: string;
|
|
@@ -1892,6 +1939,9 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
1892
1939
|
readonly params: {
|
|
1893
1940
|
readonly runId: string;
|
|
1894
1941
|
};
|
|
1942
|
+
readonly query: {
|
|
1943
|
+
readonly ownerUserId?: string | undefined;
|
|
1944
|
+
};
|
|
1895
1945
|
readonly responseMode?: Mode;
|
|
1896
1946
|
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
1897
1947
|
readonly files: readonly {
|
|
@@ -1906,6 +1956,9 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
1906
1956
|
readonly runId: string;
|
|
1907
1957
|
readonly sequence: string;
|
|
1908
1958
|
};
|
|
1959
|
+
readonly query: {
|
|
1960
|
+
readonly ownerUserId?: string | undefined;
|
|
1961
|
+
};
|
|
1909
1962
|
readonly responseMode?: Mode;
|
|
1910
1963
|
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
1911
1964
|
readonly eventId: string;
|
|
@@ -1929,6 +1982,9 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
1929
1982
|
readonly params: {
|
|
1930
1983
|
readonly runId: string;
|
|
1931
1984
|
};
|
|
1985
|
+
readonly query: {
|
|
1986
|
+
readonly ownerUserId?: string | undefined;
|
|
1987
|
+
};
|
|
1932
1988
|
readonly responseMode?: Mode;
|
|
1933
1989
|
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
1934
1990
|
readonly runId: string;
|
|
@@ -1950,6 +2006,7 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
1950
2006
|
readonly runId: string;
|
|
1951
2007
|
};
|
|
1952
2008
|
readonly query: {
|
|
2009
|
+
readonly ownerUserId?: string | undefined;
|
|
1953
2010
|
readonly processId: string;
|
|
1954
2011
|
readonly stream: "pty" | "stderr" | "stdout";
|
|
1955
2012
|
readonly atSequence?: string | undefined;
|
|
@@ -1968,6 +2025,7 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
1968
2025
|
readonly runId: string;
|
|
1969
2026
|
};
|
|
1970
2027
|
readonly query: {
|
|
2028
|
+
readonly ownerUserId?: string | undefined;
|
|
1971
2029
|
readonly fromSequence?: string | undefined;
|
|
1972
2030
|
readonly toSequence?: string | undefined;
|
|
1973
2031
|
readonly limit?: string | undefined;
|
|
@@ -2364,6 +2422,33 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
2364
2422
|
readonly status: "ok";
|
|
2365
2423
|
}, Mode>, import("effect/unstable/http/HttpClientError").HttpClientError | ([Mode] extends ["response-only"] ? never : import("effect/Schema").SchemaError), [Mode] extends ["response-only"] ? never : never>;
|
|
2366
2424
|
};
|
|
2425
|
+
readonly users: {
|
|
2426
|
+
readonly ensureUser: <Mode extends import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode = import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode>(request: {
|
|
2427
|
+
readonly payload: {
|
|
2428
|
+
readonly email: string;
|
|
2429
|
+
readonly name: string;
|
|
2430
|
+
readonly userId?: string | undefined;
|
|
2431
|
+
};
|
|
2432
|
+
readonly responseMode?: Mode;
|
|
2433
|
+
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
2434
|
+
readonly userId: string;
|
|
2435
|
+
readonly email: string;
|
|
2436
|
+
readonly name: string;
|
|
2437
|
+
readonly createdAt: string;
|
|
2438
|
+
readonly created: boolean;
|
|
2439
|
+
}, Mode>, import("effect/unstable/http/HttpClientError").HttpClientError | ([Mode] extends ["response-only"] ? never : import("effect/Schema").SchemaError | import("@sealant/api-contracts").UserBadRequestError | import("@sealant/api-contracts").UserInternalServerError), [Mode] extends ["response-only"] ? never : never>;
|
|
2440
|
+
readonly getUser: <Mode extends import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode = import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode>(request: {
|
|
2441
|
+
readonly params: {
|
|
2442
|
+
readonly userId: string;
|
|
2443
|
+
};
|
|
2444
|
+
readonly responseMode?: Mode;
|
|
2445
|
+
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
2446
|
+
readonly userId: string;
|
|
2447
|
+
readonly email: string;
|
|
2448
|
+
readonly name: string;
|
|
2449
|
+
readonly createdAt: string;
|
|
2450
|
+
}, Mode>, import("effect/unstable/http/HttpClientError").HttpClientError | ([Mode] extends ["response-only"] ? never : import("effect/Schema").SchemaError | import("@sealant/api-contracts").UserInternalServerError | import("@sealant/api-contracts").UserNotFoundError), [Mode] extends ["response-only"] ? never : never>;
|
|
2451
|
+
};
|
|
2367
2452
|
readonly workspaces: {
|
|
2368
2453
|
readonly createWorkspace: <Mode extends import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode = import("effect/unstable/httpapi/HttpApiEndpoint").ClientResponseMode>(request: {
|
|
2369
2454
|
readonly headers: {
|
|
@@ -2462,6 +2547,9 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
2462
2547
|
readonly params: {
|
|
2463
2548
|
readonly workspaceId: string;
|
|
2464
2549
|
};
|
|
2550
|
+
readonly query: {
|
|
2551
|
+
readonly ownerUserId?: string | undefined;
|
|
2552
|
+
};
|
|
2465
2553
|
readonly responseMode?: Mode;
|
|
2466
2554
|
}) => Effect.Effect<HttpApiClient.Client.Response<{
|
|
2467
2555
|
readonly workspaceId: string;
|
|
@@ -37,7 +37,7 @@ const execWorkspaceEffect = (ctx, init, argv, options) => Effect.gen(function* (
|
|
|
37
37
|
}));
|
|
38
38
|
}
|
|
39
39
|
yield* Effect.sleep(POLL_INTERVAL);
|
|
40
|
-
wire = yield* getRunOp(runId);
|
|
40
|
+
wire = yield* getRunOp(runId, ctx.config.hostLocal.ownerUserId);
|
|
41
41
|
}
|
|
42
42
|
// Exec framing: "completed" means every command executed and was recorded — anything else means
|
|
43
43
|
// the machinery broke and the exit code cannot be trusted, which IS the error case.
|
|
@@ -48,7 +48,7 @@ const execWorkspaceEffect = (ctx, init, argv, options) => Effect.gen(function* (
|
|
|
48
48
|
const processId = findCommandProcessId(started, executable);
|
|
49
49
|
const stdout = processId === undefined ? "" : yield* readScrollback(runId, processId, "stdout");
|
|
50
50
|
const stderr = processId === undefined ? "" : yield* readScrollback(runId, processId, "stderr");
|
|
51
|
-
const changes = toRunChangesData(yield* getRunChangesOp(runId));
|
|
51
|
+
const changes = toRunChangesData(yield* getRunChangesOp(runId, ctx.config.hostLocal.ownerUserId));
|
|
52
52
|
return {
|
|
53
53
|
exitCode: wire.exitCode ?? -1,
|
|
54
54
|
stdout,
|
package/dist/effect/index.d.ts
CHANGED
|
@@ -24,4 +24,4 @@ export { makeSdkRuntime } from "./runtime.js";
|
|
|
24
24
|
export type { SdkRuntime, SdkServices } from "./runtime.js";
|
|
25
25
|
export { resolveInternalConfig } from "../internal/config.js";
|
|
26
26
|
export type { SealantHostLocalConfig, SealantInternalConfig } from "../internal/config.js";
|
|
27
|
-
export { InferenceBadRequestError, InferenceConflictError, InferenceInternalServerError, InferenceNotFoundError, InferenceUnavailableError, RunBadRequestError, RunInternalServerError, RunNotFoundError, WorkspaceBadGatewayError, WorkspaceBadRequestError, WorkspaceConflictError, WorkspaceForbiddenError, WorkspaceInternalServerError, WorkspaceNotFoundError, WorkspaceServiceUnavailableError, WorkspaceUnauthorizedError, } from "@sealant/api-contracts";
|
|
27
|
+
export { ConnectedAccountBadRequestError, ConnectedAccountConflictError, ConnectedAccountInternalServerError, ConnectedAccountNotFoundError, ConnectedAccountServiceUnavailableError, InferenceBadRequestError, InferenceConflictError, InferenceInternalServerError, InferenceNotFoundError, InferenceUnavailableError, RunBadRequestError, RunInternalServerError, RunNotFoundError, UserBadRequestError, UserInternalServerError, UserNotFoundError, WorkspaceBadGatewayError, WorkspaceBadRequestError, WorkspaceConflictError, WorkspaceForbiddenError, WorkspaceInternalServerError, WorkspaceNotFoundError, WorkspaceServiceUnavailableError, WorkspaceUnauthorizedError, } from "@sealant/api-contracts";
|
package/dist/effect/index.js
CHANGED
|
@@ -28,4 +28,4 @@ export { resolveInternalConfig } from "../internal/config.js";
|
|
|
28
28
|
// The typed contract errors carried on the client's failure channel (workspaces + runs +
|
|
29
29
|
// inference — the groups the operations above call). Re-exported so Effect consumers don't need to
|
|
30
30
|
// depend on the contracts package directly to `Effect.catchTag` a failure.
|
|
31
|
-
export { InferenceBadRequestError, InferenceConflictError, InferenceInternalServerError, InferenceNotFoundError, InferenceUnavailableError, RunBadRequestError, RunInternalServerError, RunNotFoundError, WorkspaceBadGatewayError, WorkspaceBadRequestError, WorkspaceConflictError, WorkspaceForbiddenError, WorkspaceInternalServerError, WorkspaceNotFoundError, WorkspaceServiceUnavailableError, WorkspaceUnauthorizedError, } from "@sealant/api-contracts";
|
|
31
|
+
export { ConnectedAccountBadRequestError, ConnectedAccountConflictError, ConnectedAccountInternalServerError, ConnectedAccountNotFoundError, ConnectedAccountServiceUnavailableError, InferenceBadRequestError, InferenceConflictError, InferenceInternalServerError, InferenceNotFoundError, InferenceUnavailableError, RunBadRequestError, RunInternalServerError, RunNotFoundError, UserBadRequestError, UserInternalServerError, UserNotFoundError, WorkspaceBadGatewayError, WorkspaceBadRequestError, WorkspaceConflictError, WorkspaceForbiddenError, WorkspaceInternalServerError, WorkspaceNotFoundError, WorkspaceServiceUnavailableError, WorkspaceUnauthorizedError, } from "@sealant/api-contracts";
|
|
@@ -37,7 +37,7 @@ export declare const createWorkspaceOp: (payload: {
|
|
|
37
37
|
readonly repository: string;
|
|
38
38
|
readonly tag: string;
|
|
39
39
|
}, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").WorkspaceBadGatewayError | import("@sealant/api-contracts").WorkspaceBadRequestError | import("@sealant/api-contracts").WorkspaceConflictError | import("@sealant/api-contracts").WorkspaceForbiddenError | import("@sealant/api-contracts").WorkspaceInternalServerError | import("@sealant/api-contracts").WorkspaceNotFoundError | import("@sealant/api-contracts").WorkspaceServiceUnavailableError, SealantApiClient>;
|
|
40
|
-
export declare const getWorkspaceOp: (workspaceId: string) => Effect.Effect<{
|
|
40
|
+
export declare const getWorkspaceOp: (workspaceId: string, ownerUserId?: string | undefined) => Effect.Effect<{
|
|
41
41
|
readonly workspaceId: string;
|
|
42
42
|
readonly name: string;
|
|
43
43
|
readonly ownerUserId: string;
|
|
@@ -194,7 +194,7 @@ export declare const createRunOp: (payload: {
|
|
|
194
194
|
readonly createdAt: string;
|
|
195
195
|
readonly updatedAt: string;
|
|
196
196
|
}, import("effect/unstable/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RunBadRequestError | import("@sealant/api-contracts").RunInternalServerError | import("@sealant/api-contracts").RunNotFoundError | import("effect/Schema").SchemaError, SealantApiClient>;
|
|
197
|
-
export declare const getRunOp: (runId: string) => Effect.Effect<{
|
|
197
|
+
export declare const getRunOp: (runId: string, ownerUserId?: string | undefined) => Effect.Effect<{
|
|
198
198
|
readonly runId: string;
|
|
199
199
|
readonly workspaceId: string;
|
|
200
200
|
readonly attemptId?: string | undefined;
|
|
@@ -284,6 +284,7 @@ export declare const updateRunOp: (runId: string, payload: {
|
|
|
284
284
|
readonly updatedAt: string;
|
|
285
285
|
}, import("effect/unstable/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RunBadRequestError | import("@sealant/api-contracts").RunInternalServerError | import("@sealant/api-contracts").RunNotFoundError | import("effect/Schema").SchemaError, SealantApiClient>;
|
|
286
286
|
export declare const getRunTimelineOp: (runId: string, query: {
|
|
287
|
+
readonly ownerUserId?: string | undefined;
|
|
287
288
|
readonly fromSequence?: string | undefined;
|
|
288
289
|
readonly toSequence?: string | undefined;
|
|
289
290
|
readonly limit?: string | undefined;
|
|
@@ -300,6 +301,7 @@ export declare const getRunTimelineOp: (runId: string, query: {
|
|
|
300
301
|
readonly confidence: number;
|
|
301
302
|
}[], import("effect/unstable/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RunBadRequestError | import("@sealant/api-contracts").RunInternalServerError | import("@sealant/api-contracts").RunNotFoundError | import("effect/Schema").SchemaError, SealantApiClient>;
|
|
302
303
|
export declare const getRunScrollbackOp: (runId: string, query: {
|
|
304
|
+
readonly ownerUserId?: string | undefined;
|
|
303
305
|
readonly processId: string;
|
|
304
306
|
readonly stream: "pty" | "stderr" | "stdout";
|
|
305
307
|
readonly atSequence?: string | undefined;
|
|
@@ -311,7 +313,7 @@ export declare const getRunScrollbackOp: (runId: string, query: {
|
|
|
311
313
|
readonly byteCount: number;
|
|
312
314
|
readonly contentBase64: string;
|
|
313
315
|
}, import("effect/unstable/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RunBadRequestError | import("@sealant/api-contracts").RunInternalServerError | import("@sealant/api-contracts").RunNotFoundError | import("effect/Schema").SchemaError, SealantApiClient>;
|
|
314
|
-
export declare const getRunLossOp: (runId: string) => Effect.Effect<{
|
|
316
|
+
export declare const getRunLossOp: (runId: string, ownerUserId?: string | undefined) => Effect.Effect<{
|
|
315
317
|
readonly runId: string;
|
|
316
318
|
readonly droppedEventCount: string;
|
|
317
319
|
readonly sequenceGapCount: number;
|
|
@@ -326,7 +328,7 @@ export declare const getRunLossOp: (runId: string) => Effect.Effect<{
|
|
|
326
328
|
readonly reason?: string | undefined;
|
|
327
329
|
}[];
|
|
328
330
|
}, import("effect/unstable/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RunInternalServerError | import("@sealant/api-contracts").RunNotFoundError | import("effect/Schema").SchemaError, SealantApiClient>;
|
|
329
|
-
export declare const getRunChangesOp: (runId: string) => Effect.Effect<{
|
|
331
|
+
export declare const getRunChangesOp: (runId: string, ownerUserId?: string | undefined) => Effect.Effect<{
|
|
330
332
|
readonly files: readonly {
|
|
331
333
|
readonly path: string;
|
|
332
334
|
readonly change: "added" | "deleted" | "modified" | "renamed";
|
|
@@ -491,6 +493,75 @@ export declare const createAccessTokenOp: (payload: {
|
|
|
491
493
|
readonly createdAt: string;
|
|
492
494
|
readonly token: string;
|
|
493
495
|
}, import("@sealant/api-contracts").AccessTokenBadRequestError | import("@sealant/api-contracts").AccessTokenInternalServerError | import("@sealant/api-contracts").AccessTokenNotFoundError | import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError, SealantApiClient>;
|
|
496
|
+
export declare const ensureUserOp: (payload: {
|
|
497
|
+
readonly email: string;
|
|
498
|
+
readonly name: string;
|
|
499
|
+
readonly userId?: string | undefined;
|
|
500
|
+
}) => Effect.Effect<{
|
|
501
|
+
readonly userId: string;
|
|
502
|
+
readonly email: string;
|
|
503
|
+
readonly name: string;
|
|
504
|
+
readonly createdAt: string;
|
|
505
|
+
readonly created: boolean;
|
|
506
|
+
}, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").UserBadRequestError | import("@sealant/api-contracts").UserInternalServerError, SealantApiClient>;
|
|
507
|
+
export declare const getUserOp: (userId: string) => Effect.Effect<{
|
|
508
|
+
readonly userId: string;
|
|
509
|
+
readonly email: string;
|
|
510
|
+
readonly name: string;
|
|
511
|
+
readonly createdAt: string;
|
|
512
|
+
}, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").UserInternalServerError | import("@sealant/api-contracts").UserNotFoundError, SealantApiClient>;
|
|
513
|
+
export declare const listConnectedAccountsOp: (ownerUserId: string) => Effect.Effect<{
|
|
514
|
+
readonly items: readonly {
|
|
515
|
+
readonly connectedAccountId: string;
|
|
516
|
+
readonly ownerUserId: string;
|
|
517
|
+
readonly provider: "claude" | "codex" | "github";
|
|
518
|
+
readonly name: string;
|
|
519
|
+
readonly kind: string;
|
|
520
|
+
readonly status: "active" | "archived" | "invalid";
|
|
521
|
+
readonly metadata: {
|
|
522
|
+
readonly [x: string]: unknown;
|
|
523
|
+
};
|
|
524
|
+
readonly connectedAt: string;
|
|
525
|
+
readonly updatedAt: string;
|
|
526
|
+
readonly lastUsedAt: string | null;
|
|
527
|
+
readonly lastSyncedAt: string | null;
|
|
528
|
+
}[];
|
|
529
|
+
}, import("@sealant/api-contracts").ConnectedAccountInternalServerError | import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError, SealantApiClient>;
|
|
530
|
+
export declare const createConnectedAccountOp: (payload: {
|
|
531
|
+
readonly ownerUserId: string;
|
|
532
|
+
readonly provider: "claude" | "codex" | "github";
|
|
533
|
+
readonly name?: string | undefined;
|
|
534
|
+
readonly secret: string;
|
|
535
|
+
}) => Effect.Effect<{
|
|
536
|
+
readonly connectedAccountId: string;
|
|
537
|
+
readonly ownerUserId: string;
|
|
538
|
+
readonly provider: "claude" | "codex" | "github";
|
|
539
|
+
readonly name: string;
|
|
540
|
+
readonly kind: string;
|
|
541
|
+
readonly status: "active" | "archived" | "invalid";
|
|
542
|
+
readonly metadata: {
|
|
543
|
+
readonly [x: string]: unknown;
|
|
544
|
+
};
|
|
545
|
+
readonly connectedAt: string;
|
|
546
|
+
readonly updatedAt: string;
|
|
547
|
+
readonly lastUsedAt: string | null;
|
|
548
|
+
readonly lastSyncedAt: string | null;
|
|
549
|
+
}, import("@sealant/api-contracts").ConnectedAccountBadRequestError | import("@sealant/api-contracts").ConnectedAccountConflictError | import("@sealant/api-contracts").ConnectedAccountInternalServerError | import("@sealant/api-contracts").ConnectedAccountNotFoundError | import("@sealant/api-contracts").ConnectedAccountServiceUnavailableError | import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError, SealantApiClient>;
|
|
550
|
+
export declare const archiveConnectedAccountOp: (connectedAccountId: string, ownerUserId: string) => Effect.Effect<{
|
|
551
|
+
readonly connectedAccountId: string;
|
|
552
|
+
readonly ownerUserId: string;
|
|
553
|
+
readonly provider: "claude" | "codex" | "github";
|
|
554
|
+
readonly name: string;
|
|
555
|
+
readonly kind: string;
|
|
556
|
+
readonly status: "active" | "archived" | "invalid";
|
|
557
|
+
readonly metadata: {
|
|
558
|
+
readonly [x: string]: unknown;
|
|
559
|
+
};
|
|
560
|
+
readonly connectedAt: string;
|
|
561
|
+
readonly updatedAt: string;
|
|
562
|
+
readonly lastUsedAt: string | null;
|
|
563
|
+
readonly lastSyncedAt: string | null;
|
|
564
|
+
}, import("@sealant/api-contracts").ConnectedAccountInternalServerError | import("@sealant/api-contracts").ConnectedAccountNotFoundError | import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError, SealantApiClient>;
|
|
494
565
|
export declare const inferenceRespondOp: (payload: {
|
|
495
566
|
readonly ownerUserId: string;
|
|
496
567
|
readonly credentials?: {
|
|
@@ -5,7 +5,10 @@ export const createWorkspaceOp = (payload, idempotencyKey) => Effect.flatMap(Sea
|
|
|
5
5
|
payload,
|
|
6
6
|
headers: idempotencyKey === undefined ? {} : { "idempotency-key": idempotencyKey },
|
|
7
7
|
}));
|
|
8
|
-
export const getWorkspaceOp = (workspaceId) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.getWorkspace({
|
|
8
|
+
export const getWorkspaceOp = (workspaceId, ownerUserId) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.getWorkspace({
|
|
9
|
+
params: { workspaceId },
|
|
10
|
+
query: ownerUserId === undefined ? {} : { ownerUserId },
|
|
11
|
+
}));
|
|
9
12
|
export const listWorkspacesOp = (query) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.listWorkspaces({ query }));
|
|
10
13
|
export const execWorkspaceOp = (workspaceId, payload) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.execWorkspace({ params: { workspaceId }, payload }));
|
|
11
14
|
export const stopWorkspaceOp = (workspaceId, payload) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.stopWorkspace({ params: { workspaceId }, payload }));
|
|
@@ -13,13 +16,14 @@ export const restartWorkspaceOp = (workspaceId, payload) => Effect.flatMap(Seala
|
|
|
13
16
|
export const expireWorkspaceOp = (workspaceId, payload) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.expireWorkspace({ params: { workspaceId }, payload }));
|
|
14
17
|
// ---- runs ----
|
|
15
18
|
export const createRunOp = (payload) => Effect.flatMap(SealantApiClient, (client) => client.runs.createRun({ payload }));
|
|
16
|
-
|
|
19
|
+
const ownerQuery = (ownerUserId) => ownerUserId === undefined ? {} : { ownerUserId };
|
|
20
|
+
export const getRunOp = (runId, ownerUserId) => Effect.flatMap(SealantApiClient, (client) => client.runs.getRun({ params: { runId }, query: ownerQuery(ownerUserId) }));
|
|
17
21
|
export const listRunsOp = (query) => Effect.flatMap(SealantApiClient, (client) => client.runs.listRuns({ query }));
|
|
18
22
|
export const updateRunOp = (runId, payload) => Effect.flatMap(SealantApiClient, (client) => client.runs.updateRun({ params: { runId }, payload }));
|
|
19
23
|
export const getRunTimelineOp = (runId, query) => Effect.flatMap(SealantApiClient, (client) => Effect.map(client.runs.getRunTimeline({ params: { runId }, query }), (r) => r.items));
|
|
20
24
|
export const getRunScrollbackOp = (runId, query) => Effect.flatMap(SealantApiClient, (client) => client.runs.getRunScrollback({ params: { runId }, query }));
|
|
21
|
-
export const getRunLossOp = (runId) => Effect.flatMap(SealantApiClient, (client) => client.runs.getRunLoss({ params: { runId } }));
|
|
22
|
-
export const getRunChangesOp = (runId) => Effect.flatMap(SealantApiClient, (client) => client.runs.getRunChanges({ params: { runId } }));
|
|
25
|
+
export const getRunLossOp = (runId, ownerUserId) => Effect.flatMap(SealantApiClient, (client) => client.runs.getRunLoss({ params: { runId }, query: ownerQuery(ownerUserId) }));
|
|
26
|
+
export const getRunChangesOp = (runId, ownerUserId) => Effect.flatMap(SealantApiClient, (client) => client.runs.getRunChanges({ params: { runId }, query: ownerQuery(ownerUserId) }));
|
|
23
27
|
// ---- sessions ----
|
|
24
28
|
export const createSessionOp = (payload) => Effect.flatMap(SealantApiClient, (client) => client.sessions.createSession({ payload, headers: {} }));
|
|
25
29
|
export const getSessionOp = (sessionId, ownerUserId) => Effect.flatMap(SealantApiClient, (client) => client.sessions.getSession({
|
|
@@ -35,5 +39,15 @@ export const signalSessionOp = (sessionId, payload) => Effect.flatMap(SealantApi
|
|
|
35
39
|
export const closeSessionOp = (sessionId, payload) => Effect.flatMap(SealantApiClient, (client) => client.sessions.closeSession({ params: { sessionId }, headers: {}, payload }));
|
|
36
40
|
// ---- access tokens ----
|
|
37
41
|
export const createAccessTokenOp = (payload) => Effect.flatMap(SealantApiClient, (client) => client.accessTokens.createAccessToken({ payload }));
|
|
42
|
+
// ---- users ----
|
|
43
|
+
export const ensureUserOp = (payload) => Effect.flatMap(SealantApiClient, (client) => client.users.ensureUser({ payload }));
|
|
44
|
+
export const getUserOp = (userId) => Effect.flatMap(SealantApiClient, (client) => client.users.getUser({ params: { userId } }));
|
|
45
|
+
// ---- connected accounts ----
|
|
46
|
+
export const listConnectedAccountsOp = (ownerUserId) => Effect.flatMap(SealantApiClient, (client) => client.connectedAccounts.listConnectedAccounts({ query: { ownerUserId } }));
|
|
47
|
+
export const createConnectedAccountOp = (payload) => Effect.flatMap(SealantApiClient, (client) => client.connectedAccounts.createConnectedAccount({ payload }));
|
|
48
|
+
export const archiveConnectedAccountOp = (connectedAccountId, ownerUserId) => Effect.flatMap(SealantApiClient, (client) => client.connectedAccounts.archiveConnectedAccount({
|
|
49
|
+
params: { connectedAccountId },
|
|
50
|
+
query: { ownerUserId },
|
|
51
|
+
}));
|
|
38
52
|
// ---- inference ----
|
|
39
53
|
export const inferenceRespondOp = (payload) => Effect.flatMap(SealantApiClient, (client) => client.inference.respond({ payload }));
|
|
@@ -38,7 +38,7 @@ const createHarnessRunEffect = (ctx, init, prompt, options) => Effect.gen(functi
|
|
|
38
38
|
...metadata,
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
-
const details = yield* getWorkspaceOp(init.id);
|
|
41
|
+
const details = yield* getWorkspaceOp(init.id, ctx.config.hostLocal.ownerUserId);
|
|
42
42
|
const spec = details.spec;
|
|
43
43
|
const harnessId = spec?.harness?.id;
|
|
44
44
|
if (harnessId === undefined) {
|
|
@@ -66,10 +66,10 @@ const runHarnessEffect = (ctx, init, prompt, options) => Effect.gen(function* ()
|
|
|
66
66
|
}));
|
|
67
67
|
}
|
|
68
68
|
yield* Effect.sleep(POLL_INTERVAL);
|
|
69
|
-
wire = yield* getRunOp(runId);
|
|
69
|
+
wire = yield* getRunOp(runId, ctx.config.hostLocal.ownerUserId);
|
|
70
70
|
}
|
|
71
71
|
// Read the changes the run produced (captured server-side).
|
|
72
|
-
const changes = toRunChangesData(yield* getRunChangesOp(runId));
|
|
72
|
+
const changes = toRunChangesData(yield* getRunChangesOp(runId, ctx.config.hostLocal.ownerUserId));
|
|
73
73
|
return makeRun(ctx, { wire, changes });
|
|
74
74
|
});
|
|
75
75
|
/** The BLOCKING `harness.run()` implementation, registered into the Workspace facade by the client. */
|
package/dist/facade/record.js
CHANGED
|
@@ -135,7 +135,10 @@ export const renderTranscript = (commands) => {
|
|
|
135
135
|
return `${blocks.join("\n\n")}\n`;
|
|
136
136
|
};
|
|
137
137
|
export const makeRunRecord = (ctx, runId) => {
|
|
138
|
-
const fetchTimeline = (from) => ctx.runtime.run(getRunTimelineOp(runId,
|
|
138
|
+
const fetchTimeline = (from) => ctx.runtime.run(getRunTimelineOp(runId, {
|
|
139
|
+
ownerUserId: ctx.config.hostLocal.ownerUserId,
|
|
140
|
+
...(from === undefined ? {} : { fromSequence: from.toString() }),
|
|
141
|
+
}));
|
|
139
142
|
return {
|
|
140
143
|
runId,
|
|
141
144
|
replay: async (options) => {
|
|
@@ -171,7 +174,10 @@ export const makeRunRecord = (ctx, runId) => {
|
|
|
171
174
|
let from = options?.from;
|
|
172
175
|
const deadline = Date.now() + STREAM_TIMEOUT_MS;
|
|
173
176
|
for (;;) {
|
|
174
|
-
const wire = await ctxRun.run(getRunTimelineOp(runId,
|
|
177
|
+
const wire = await ctxRun.run(getRunTimelineOp(runId, {
|
|
178
|
+
ownerUserId: ctx.config.hostLocal.ownerUserId,
|
|
179
|
+
...(from === undefined ? {} : { fromSequence: from.toString() }),
|
|
180
|
+
}));
|
|
175
181
|
for (const entry of wire) {
|
|
176
182
|
const mapped = toTimelineEntry(entry);
|
|
177
183
|
yield mapped;
|
|
@@ -179,9 +185,12 @@ export const makeRunRecord = (ctx, runId) => {
|
|
|
179
185
|
}
|
|
180
186
|
// Stop once the run is terminal — with one final drain to catch entries written between the
|
|
181
187
|
// last timeline fetch and the status check.
|
|
182
|
-
const run = await ctxRun.run(getRunOp(runId));
|
|
188
|
+
const run = await ctxRun.run(getRunOp(runId, ctx.config.hostLocal.ownerUserId));
|
|
183
189
|
if (TERMINAL_RUN_STATUSES.has(run.status)) {
|
|
184
|
-
const tail = await ctxRun.run(getRunTimelineOp(runId,
|
|
190
|
+
const tail = await ctxRun.run(getRunTimelineOp(runId, {
|
|
191
|
+
ownerUserId: ctx.config.hostLocal.ownerUserId,
|
|
192
|
+
...(from === undefined ? {} : { fromSequence: from.toString() }),
|
|
193
|
+
}));
|
|
185
194
|
for (const entry of tail) {
|
|
186
195
|
yield toTimelineEntry(entry);
|
|
187
196
|
}
|
|
@@ -208,7 +217,11 @@ export const makeRunRecord = (ctx, runId) => {
|
|
|
208
217
|
scrollback: (processId, stream) => {
|
|
209
218
|
const run = ctx.runtime;
|
|
210
219
|
async function* iterate() {
|
|
211
|
-
const response = await run.run(getRunScrollbackOp(runId, {
|
|
220
|
+
const response = await run.run(getRunScrollbackOp(runId, {
|
|
221
|
+
ownerUserId: ctx.config.hostLocal.ownerUserId,
|
|
222
|
+
processId,
|
|
223
|
+
stream,
|
|
224
|
+
}));
|
|
212
225
|
const bytes = Buffer.from(response.contentBase64, "base64");
|
|
213
226
|
if (bytes.byteLength > 0) {
|
|
214
227
|
yield new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
@@ -216,9 +229,9 @@ export const makeRunRecord = (ctx, runId) => {
|
|
|
216
229
|
}
|
|
217
230
|
return iterate();
|
|
218
231
|
},
|
|
219
|
-
loss: async () => toLossReport(await ctx.runtime.run(getRunLossOp(runId))),
|
|
232
|
+
loss: async () => toLossReport(await ctx.runtime.run(getRunLossOp(runId, ctx.config.hostLocal.ownerUserId))),
|
|
220
233
|
summary: async () => {
|
|
221
|
-
const run = await ctx.runtime.run(getRunOp(runId));
|
|
234
|
+
const run = await ctx.runtime.run(getRunOp(runId, ctx.config.hostLocal.ownerUserId));
|
|
222
235
|
const timeline = await fetchTimeline();
|
|
223
236
|
const durationMs = run.startedAt !== undefined && run.finishedAt !== undefined
|
|
224
237
|
? new Date(run.finishedAt).getTime() - new Date(run.startedAt).getTime()
|
package/dist/facade/run.js
CHANGED
|
@@ -46,11 +46,12 @@ export const makeRun = (ctx, init) => {
|
|
|
46
46
|
});
|
|
47
47
|
}
|
|
48
48
|
await delay(WAIT_POLL_INTERVAL_MS);
|
|
49
|
-
current = await ctx.runtime.run(getRunOp(runId));
|
|
49
|
+
current = await ctx.runtime.run(getRunOp(runId, ctx.config.hostLocal.ownerUserId));
|
|
50
50
|
}
|
|
51
51
|
// Settle the changes: a handle from `harness.start()` or `runs.get()` has none captured yet,
|
|
52
52
|
// so read the server-side capture now that the run is terminal.
|
|
53
|
-
const settledChanges = changesData ??
|
|
53
|
+
const settledChanges = changesData ??
|
|
54
|
+
toRunChangesData(await ctx.runtime.run(getRunChangesOp(runId, ctx.config.hostLocal.ownerUserId)));
|
|
54
55
|
return makeRun(ctx, { wire: current, changes: settledChanges });
|
|
55
56
|
},
|
|
56
57
|
};
|
package/dist/facade/session.js
CHANGED
|
@@ -3,18 +3,19 @@ import { closeSessionOp, getSessionOp, getSessionOutputOp, resizeSessionOp, send
|
|
|
3
3
|
* Open the held-WebSocket terminal attachment (the data plane). One socket:
|
|
4
4
|
* binary frames are PTY bytes in both directions, text frames are control
|
|
5
5
|
* JSON (`{"t":"resize",...}` up, `{"t":"end"}` down). Auth rides the connect —
|
|
6
|
-
* `?token=` for apiKey clients (WebSocket cannot
|
|
7
|
-
*
|
|
6
|
+
* `?ownerUserId=` always, plus `?token=` for apiKey clients (WebSocket cannot
|
|
7
|
+
* set headers; a service principal needs the owner assertion *and* its key) —
|
|
8
|
+
* and never repeats per event.
|
|
8
9
|
*/
|
|
9
10
|
const openAttachment = (ctx, sessionId, options) => {
|
|
10
11
|
const config = ctx.config;
|
|
11
12
|
const url = new URL(`/v1/sessions/${sessionId}/attach`, config.baseUrl);
|
|
12
13
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
13
14
|
url.searchParams.set("from", (options?.from ?? 0n).toString());
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
// The owner assertion always rides the URL; a service principal needs it *alongside* its key,
|
|
16
|
+
// and WebSocket cannot carry headers, so the key rides the URL too.
|
|
17
|
+
url.searchParams.set("ownerUserId", config.hostLocal.ownerUserId);
|
|
18
|
+
if (config.apiKey !== undefined) {
|
|
18
19
|
url.searchParams.set("token", config.apiKey);
|
|
19
20
|
}
|
|
20
21
|
return new Promise((resolve, reject) => {
|
|
@@ -126,9 +127,7 @@ async function* streamOverSse(ctx, sessionId, from, signal) {
|
|
|
126
127
|
const fetchImpl = config.fetch ?? fetch;
|
|
127
128
|
const url = new URL(`/v1/sessions/${sessionId}/output/stream`, config.baseUrl);
|
|
128
129
|
url.searchParams.set("from", from.toString());
|
|
129
|
-
|
|
130
|
-
url.searchParams.set("ownerUserId", config.hostLocal.ownerUserId);
|
|
131
|
-
}
|
|
130
|
+
url.searchParams.set("ownerUserId", config.hostLocal.ownerUserId);
|
|
132
131
|
const response = await fetchImpl(url, {
|
|
133
132
|
headers: {
|
|
134
133
|
accept: "text/event-stream",
|
package/dist/facade/workspace.js
CHANGED
|
@@ -63,7 +63,7 @@ export const makeWorkspace = (ctx, init) => {
|
|
|
63
63
|
if (init.harness !== undefined) {
|
|
64
64
|
return [init.harness.launchCommand ?? init.harness.id];
|
|
65
65
|
}
|
|
66
|
-
const details = await ctx.runtime.run(getWorkspaceOp(init.id));
|
|
66
|
+
const details = await ctx.runtime.run(getWorkspaceOp(init.id, ctx.config.hostLocal.ownerUserId));
|
|
67
67
|
const spec = details.spec;
|
|
68
68
|
const harnessId = spec?.harness?.id;
|
|
69
69
|
if (harnessId === undefined) {
|
|
@@ -93,13 +93,13 @@ export const makeWorkspace = (ctx, init) => {
|
|
|
93
93
|
id: init.id,
|
|
94
94
|
name: init.name,
|
|
95
95
|
status: async () => {
|
|
96
|
-
const details = await ctx.runtime.run(getWorkspaceOp(init.id));
|
|
96
|
+
const details = await ctx.runtime.run(getWorkspaceOp(init.id, ctx.config.hostLocal.ownerUserId));
|
|
97
97
|
return details.status;
|
|
98
98
|
},
|
|
99
99
|
ready: async () => {
|
|
100
100
|
const deadline = Date.now() + READY_TIMEOUT_MS;
|
|
101
101
|
for (;;) {
|
|
102
|
-
const details = await ctx.runtime.run(getWorkspaceOp(init.id));
|
|
102
|
+
const details = await ctx.runtime.run(getWorkspaceOp(init.id, ctx.config.hostLocal.ownerUserId));
|
|
103
103
|
// Gate on the coarse "ready" status, which the control plane now emits ONLY after the
|
|
104
104
|
// in-workspace daemon's control socket is accepting (readiness probe in the launch path).
|
|
105
105
|
// This is honest: when ready() resolves, harness.run() can connect without racing the socket.
|
|
@@ -128,7 +128,7 @@ export const makeWorkspace = (ctx, init) => {
|
|
|
128
128
|
let lastStatus;
|
|
129
129
|
const deadline = Date.now() + READY_TIMEOUT_MS;
|
|
130
130
|
for (;;) {
|
|
131
|
-
const details = await ctxRun.run(getWorkspaceOp(init.id));
|
|
131
|
+
const details = await ctxRun.run(getWorkspaceOp(init.id, ctx.config.hostLocal.ownerUserId));
|
|
132
132
|
if (details.status !== lastStatus) {
|
|
133
133
|
lastStatus = details.status;
|
|
134
134
|
yield {
|
|
@@ -156,7 +156,7 @@ export const makeWorkspace = (ctx, init) => {
|
|
|
156
156
|
await ctx.runtime.run(stopWorkspaceOp(init.id, { ownerUserId }));
|
|
157
157
|
const deadline = Date.now() + STOP_TIMEOUT_MS;
|
|
158
158
|
for (;;) {
|
|
159
|
-
const details = await ctx.runtime.run(getWorkspaceOp(init.id));
|
|
159
|
+
const details = await ctx.runtime.run(getWorkspaceOp(init.id, ctx.config.hostLocal.ownerUserId));
|
|
160
160
|
if (details.status === "stopped") {
|
|
161
161
|
return;
|
|
162
162
|
}
|
|
@@ -214,10 +214,10 @@ const openForward = (ctx, workspaceId, port, options) => {
|
|
|
214
214
|
if (options?.protocol === "udp") {
|
|
215
215
|
url.searchParams.set("protocol", "udp");
|
|
216
216
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
217
|
+
// The owner assertion always rides the URL (a service principal needs it alongside its
|
|
218
|
+
// key, and WebSocket cannot carry headers — same contract as the session attach).
|
|
219
|
+
url.searchParams.set("ownerUserId", config.hostLocal.ownerUserId);
|
|
220
|
+
if (config.apiKey !== undefined) {
|
|
221
221
|
url.searchParams.set("token", config.apiKey);
|
|
222
222
|
}
|
|
223
223
|
return new Promise((resolve, reject) => {
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* no contract change. The public `repository` is the SOURCE git repo (it becomes
|
|
5
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
|
-
* connects to), the runtime target is
|
|
7
|
+
* connects to), the runtime target is `auto` (the deployment's default adapter — Docker on self-host, Kubernetes
|
|
8
|
+
* when the worker is configured for a cluster), and the
|
|
8
9
|
* foreground is a keepalive so the workspace idles with the daemon up and the harness is exec'd on
|
|
9
10
|
* demand by `run()` rather than launched at boot. `options.credentials`, if present, is lowered via
|
|
10
11
|
* `mapWorkspaceCredentials` (see `./credentials.js`) and folded into `spec.credentials`; the control
|
|
@@ -182,7 +183,9 @@ export const buildCreateWorkspaceRequest = (options, config) => {
|
|
|
182
183
|
os: options.baseImage !== undefined
|
|
183
184
|
? { family: "custom", mode: "require", baseImage: options.baseImage }
|
|
184
185
|
: { family: options.os ?? "fedora", mode: "prefer" },
|
|
185
|
-
|
|
186
|
+
// `auto` = the deployment's DEFAULT_RUNTIME_ADAPTER. SDK callers don't know (and must not
|
|
187
|
+
// care) whether the control plane runs workspaces as containers or Pods.
|
|
188
|
+
runtime: { family: "auto", mode: "prefer" },
|
|
186
189
|
},
|
|
187
190
|
lifecycle: {
|
|
188
191
|
startup: { foreground: { kind: "command", run: "sleep infinity", shell: "bash" } },
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The PUBLIC surface (`SealantConfig` in `../types.ts`) is intentionally minimal: `{ baseUrl, apiKey }`.
|
|
5
5
|
* The SDK is now a thin HTTP client (run execution + telemetry moved server-side), so the only
|
|
6
|
-
* host-local concerns left are
|
|
7
|
-
* payloads.
|
|
8
|
-
*
|
|
6
|
+
* host-local concerns left are the owner principal and the registry id used on create/run
|
|
7
|
+
* payloads. The owner comes from `SealantConfig.ownerUserId` when a product acts on behalf of a
|
|
8
|
+
* specific user (one client per user), else from the environment with docker-compose defaults.
|
|
9
9
|
*/
|
|
10
10
|
import type { SealantConfig } from "../types.js";
|
|
11
11
|
export interface SealantHostLocalConfig {
|
package/dist/internal/config.js
CHANGED
|
@@ -11,7 +11,7 @@ export const resolveInternalConfig = (config) => ({
|
|
|
11
11
|
apiKey: config.apiKey,
|
|
12
12
|
fetch: config.fetch,
|
|
13
13
|
hostLocal: {
|
|
14
|
-
ownerUserId: env("SEALANT_OWNER_USER_ID") ?? DEFAULT_OWNER_USER_ID,
|
|
14
|
+
ownerUserId: config.ownerUserId ?? env("SEALANT_OWNER_USER_ID") ?? DEFAULT_OWNER_USER_ID,
|
|
15
15
|
registryId: env("SEALANT_REGISTRY_ID") ?? DEFAULT_REGISTRY_ID,
|
|
16
16
|
},
|
|
17
17
|
});
|
package/dist/types.d.ts
CHANGED
|
@@ -23,8 +23,18 @@
|
|
|
23
23
|
export interface SealantConfig {
|
|
24
24
|
/** Base URL of the Sealant control-plane API (e.g. `http://localhost:8080`). */
|
|
25
25
|
readonly baseUrl: string;
|
|
26
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Bearer secret for authenticated deployments: a SERVICE KEY (`SEALANT_SERVICE_KEYS` on the
|
|
28
|
+
* control plane — lets this client act on behalf of any `ownerUserId`) or a scoped user access
|
|
29
|
+
* token (session surface only). Optional for a localhost demo with no auth.
|
|
30
|
+
*/
|
|
27
31
|
readonly apiKey?: string;
|
|
32
|
+
/**
|
|
33
|
+
* The user every call is attributed to. A product that owns its own login builds ONE client per
|
|
34
|
+
* user with that user's Sealant id (see `users.ensure`). Defaults to `SEALANT_OWNER_USER_ID`,
|
|
35
|
+
* then `usr_local`.
|
|
36
|
+
*/
|
|
37
|
+
readonly ownerUserId?: string;
|
|
28
38
|
/** Override the `fetch` implementation (tests, custom agents, proxies). */
|
|
29
39
|
readonly fetch?: typeof fetch;
|
|
30
40
|
}
|
|
@@ -893,3 +903,65 @@ export interface InferenceContinueOptions {
|
|
|
893
903
|
export interface InferenceNamespace {
|
|
894
904
|
respond(options: InferenceRespondOptions | InferenceContinueOptions): Promise<InferenceResponse>;
|
|
895
905
|
}
|
|
906
|
+
export interface SealantUser {
|
|
907
|
+
readonly userId: string;
|
|
908
|
+
readonly email: string;
|
|
909
|
+
readonly name: string;
|
|
910
|
+
readonly createdAt: string;
|
|
911
|
+
}
|
|
912
|
+
export interface EnsureUserOptions {
|
|
913
|
+
readonly email: string;
|
|
914
|
+
readonly name: string;
|
|
915
|
+
/** Caller-chosen id for a NEW user; ignored when the email already exists. */
|
|
916
|
+
readonly userId?: string;
|
|
917
|
+
}
|
|
918
|
+
export interface EnsuredUser extends SealantUser {
|
|
919
|
+
/** True when this call created the user. */
|
|
920
|
+
readonly created: boolean;
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Identity rows for products that own their own login. `ensure` is idempotent on email: call it on
|
|
924
|
+
* every sign-in and build the per-user client with the returned `userId` as `ownerUserId`.
|
|
925
|
+
*/
|
|
926
|
+
export interface UsersNamespace {
|
|
927
|
+
ensure(options: EnsureUserOptions): Promise<EnsuredUser>;
|
|
928
|
+
get(userId: string): Promise<SealantUser>;
|
|
929
|
+
}
|
|
930
|
+
export type ConnectedAccountProvider = "claude" | "codex" | "github";
|
|
931
|
+
export type ConnectedAccountStatus = "active" | "invalid" | "archived";
|
|
932
|
+
/** A connected account as every surface sees it — NEVER carries secret material. */
|
|
933
|
+
export interface ConnectedAccount {
|
|
934
|
+
readonly connectedAccountId: string;
|
|
935
|
+
readonly ownerUserId: string;
|
|
936
|
+
readonly provider: ConnectedAccountProvider;
|
|
937
|
+
readonly name: string;
|
|
938
|
+
/** Provider-shaped payload kind: oauth-token | credentials-json | auth-json | gh-cli-token. */
|
|
939
|
+
readonly kind: string;
|
|
940
|
+
readonly status: ConnectedAccountStatus;
|
|
941
|
+
/** Non-secret display data (token suffix, codex account email, github login + scopes). */
|
|
942
|
+
readonly metadata: Readonly<Record<string, unknown>>;
|
|
943
|
+
readonly connectedAt: string;
|
|
944
|
+
readonly updatedAt: string;
|
|
945
|
+
readonly lastUsedAt: string | null;
|
|
946
|
+
readonly lastSyncedAt: string | null;
|
|
947
|
+
}
|
|
948
|
+
export interface ConnectConnectedAccountOptions {
|
|
949
|
+
readonly provider: ConnectedAccountProvider;
|
|
950
|
+
/**
|
|
951
|
+
* Provider-shaped plaintext, passed through and sealed server-side: a Claude setup token or
|
|
952
|
+
* verbatim `.credentials.json`, verbatim Codex `auth.json`, or a GitHub token. Never logged.
|
|
953
|
+
*/
|
|
954
|
+
readonly secret: string;
|
|
955
|
+
/** Account name under the provider; defaults to `default` (the one `credentials: { x: true }` picks). */
|
|
956
|
+
readonly name?: string;
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* The owner's connected provider accounts. `connect` upserts on (provider, name), so reconnecting
|
|
960
|
+
* swaps the sealed credential in place. Secrets flow one way — in; no call returns them.
|
|
961
|
+
*/
|
|
962
|
+
export interface ConnectedAccountsNamespace {
|
|
963
|
+
list(): Promise<readonly ConnectedAccount[]>;
|
|
964
|
+
connect(options: ConnectConnectedAccountOptions): Promise<ConnectedAccount>;
|
|
965
|
+
/** Soft-archives the account; uniform not-found for "does not exist" and "not yours". */
|
|
966
|
+
disconnect(connectedAccountId: string): Promise<ConnectedAccount>;
|
|
967
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sealant/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.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.23.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@effect/vitest": "4.0.0-beta.85",
|