@sealant/sdk-next 0.40.0-next.761 → 0.40.0-next.764
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.js +13 -1
- package/dist/effect/api-client.d.ts +92 -2
- package/dist/effect/operations.d.ts +33 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.js +13 -0
- package/dist/facade/bounded-forward.d.ts +7 -0
- package/dist/facade/bounded-forward.js +191 -0
- package/dist/facade/forward.d.ts +3 -12
- package/dist/facade/forward.js +25 -20
- package/dist/facade/workspace.js +18 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/features.d.ts +2 -0
- package/dist/internal/features.js +7 -0
- package/dist/types.d.ts +107 -7
- package/package.json +3 -3
package/dist/client.js
CHANGED
|
@@ -7,7 +7,7 @@ import { makeWorkspace, registerHarnessExecutors, toRuntimeInfo, toWorkspaceImag
|
|
|
7
7
|
import { buildCreateWorkspaceRequest } from "./internal/blueprint.js";
|
|
8
8
|
import { resolveInternalConfig } from "./internal/config.js";
|
|
9
9
|
import { parseTtlSeconds } from "./internal/duration.js";
|
|
10
|
-
import { readFeatures, requireConnectedAccountDefaults } from "./internal/features.js";
|
|
10
|
+
import { readFeatures, requireConnectedAccountDefaults, requireConnectedAccountLoginClass, } from "./internal/features.js";
|
|
11
11
|
import { imageSpecKey } from "./internal/image-key.js";
|
|
12
12
|
import { buildInferenceRespondRequest, mapInferenceResponse } from "./internal/inference.js";
|
|
13
13
|
import { requireSshUser } from "./internal/process-user.js";
|
|
@@ -35,6 +35,7 @@ const mapConnectedAccount = (wire) => ({
|
|
|
35
35
|
credential: wire.credential,
|
|
36
36
|
isDefault: wire.isDefault ?? wire.name === "default",
|
|
37
37
|
...(wire.credentialRevision === undefined ? {} : { credentialRevision: wire.credentialRevision }),
|
|
38
|
+
...(wire.login === undefined ? {} : { login: wire.login }),
|
|
38
39
|
});
|
|
39
40
|
// Wire the run-execution implementations into the Workspace facade (the injection point exists to
|
|
40
41
|
// break the workspace <-> run-harness import cycle; the client is the composition root).
|
|
@@ -244,10 +245,20 @@ export class Sealant {
|
|
|
244
245
|
const wire = await this.#runtime.run(listConnectedAccountsOp(this.#ctx.config.hostLocal.ownerUserId));
|
|
245
246
|
return wire.items.map(mapConnectedAccount);
|
|
246
247
|
},
|
|
248
|
+
listWithHistory: async () => {
|
|
249
|
+
const wire = await this.#runtime.run(listConnectedAccountsOp(this.#ctx.config.hostLocal.ownerUserId));
|
|
250
|
+
return {
|
|
251
|
+
accounts: wire.items.map(mapConnectedAccount),
|
|
252
|
+
...(wire.hasApiKeyHistory === undefined ? {} : { hasApiKeyHistory: wire.hasApiKeyHistory }),
|
|
253
|
+
};
|
|
254
|
+
},
|
|
247
255
|
connect: async (options) => {
|
|
248
256
|
if (options.accountId !== undefined || options.makeDefault !== undefined) {
|
|
249
257
|
await requireConnectedAccountDefaults(this.#ctx);
|
|
250
258
|
}
|
|
259
|
+
if (options.login !== undefined) {
|
|
260
|
+
await requireConnectedAccountLoginClass(this.#ctx);
|
|
261
|
+
}
|
|
251
262
|
const wire = await this.#runtime.run(createConnectedAccountOp({
|
|
252
263
|
ownerUserId: this.#ctx.config.hostLocal.ownerUserId,
|
|
253
264
|
provider: options.provider,
|
|
@@ -255,6 +266,7 @@ export class Sealant {
|
|
|
255
266
|
...(options.name === undefined ? {} : { name: options.name }),
|
|
256
267
|
...(options.accountId === undefined ? {} : { accountId: options.accountId }),
|
|
257
268
|
...(options.makeDefault === undefined ? {} : { makeDefault: options.makeDefault }),
|
|
269
|
+
...(options.login === undefined ? {} : { login: options.login }),
|
|
258
270
|
}));
|
|
259
271
|
return mapConnectedAccount(wire);
|
|
260
272
|
},
|
|
@@ -76,6 +76,12 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
76
76
|
}>;
|
|
77
77
|
readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
78
78
|
readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
|
|
79
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
80
|
+
readonly kind: import("effect/Schema").Literal<"subscription">;
|
|
81
|
+
}>, import("effect/Schema").Struct<{
|
|
82
|
+
readonly kind: import("effect/Schema").Literal<"api-key">;
|
|
83
|
+
readonly suffix: import("effect/Schema").String;
|
|
84
|
+
}>]>>;
|
|
79
85
|
}>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ConnectedAccountNotFoundError | typeof import("@sealant/api-contracts").ConnectedAccountInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
80
86
|
readonly createConnectedAccount: HttpApiClient.Client.Method<import("effect/http-api/HttpApiEndpoint").HttpApiEndpoint<"createConnectedAccount", "POST", "/v1/connected-accounts/", never, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
|
|
81
87
|
readonly ownerUserId: import("effect/Schema").String;
|
|
@@ -83,6 +89,7 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
83
89
|
readonly name: import("effect/Schema").optional<import("effect/Schema").String>;
|
|
84
90
|
readonly accountId: import("effect/Schema").optional<import("effect/Schema").String>;
|
|
85
91
|
readonly makeDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
92
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Literals<readonly ["subscription", "api-key"]>>;
|
|
86
93
|
readonly secret: import("effect/Schema").String;
|
|
87
94
|
}>>, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
|
|
88
95
|
readonly connectedAccountId: import("effect/Schema").String;
|
|
@@ -104,6 +111,12 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
104
111
|
}>;
|
|
105
112
|
readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
106
113
|
readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
|
|
114
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
115
|
+
readonly kind: import("effect/Schema").Literal<"subscription">;
|
|
116
|
+
}>, import("effect/Schema").Struct<{
|
|
117
|
+
readonly kind: import("effect/Schema").Literal<"api-key">;
|
|
118
|
+
readonly suffix: import("effect/Schema").String;
|
|
119
|
+
}>]>>;
|
|
107
120
|
}>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ConnectedAccountBadRequestError | typeof import("@sealant/api-contracts").ConnectedAccountNotFoundError | typeof import("@sealant/api-contracts").ConnectedAccountConflictError | typeof import("@sealant/api-contracts").ConnectedAccountServiceUnavailableError | typeof import("@sealant/api-contracts").ConnectedAccountInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
108
121
|
readonly listConnectedAccounts: HttpApiClient.Client.Method<import("effect/http-api/HttpApiEndpoint").HttpApiEndpoint<"listConnectedAccounts", "GET", "/v1/connected-accounts/", never, import("effect/Schema").toCodecStringTree<import("effect/Schema").Struct<{
|
|
109
122
|
readonly ownerUserId: import("effect/Schema").String;
|
|
@@ -128,7 +141,14 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
128
141
|
}>;
|
|
129
142
|
readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
130
143
|
readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
|
|
131
|
-
|
|
144
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
145
|
+
readonly kind: import("effect/Schema").Literal<"subscription">;
|
|
146
|
+
}>, import("effect/Schema").Struct<{
|
|
147
|
+
readonly kind: import("effect/Schema").Literal<"api-key">;
|
|
148
|
+
readonly suffix: import("effect/Schema").String;
|
|
149
|
+
}>]>>;
|
|
150
|
+
}>>;
|
|
151
|
+
readonly hasApiKeyHistory: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
132
152
|
}>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ConnectedAccountInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
133
153
|
readonly markConnectedAccountInvalid: HttpApiClient.Client.Method<import("effect/http-api/HttpApiEndpoint").HttpApiEndpoint<"markConnectedAccountInvalid", "POST", "/v1/connected-accounts/:connectedAccountId/mark-invalid", import("effect/Schema").toCodecStringTree<import("effect/Schema").Struct<{
|
|
134
154
|
readonly connectedAccountId: import("effect/Schema").String;
|
|
@@ -155,6 +175,12 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
155
175
|
}>;
|
|
156
176
|
readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
157
177
|
readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
|
|
178
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
179
|
+
readonly kind: import("effect/Schema").Literal<"subscription">;
|
|
180
|
+
}>, import("effect/Schema").Struct<{
|
|
181
|
+
readonly kind: import("effect/Schema").Literal<"api-key">;
|
|
182
|
+
readonly suffix: import("effect/Schema").String;
|
|
183
|
+
}>]>>;
|
|
158
184
|
readonly outcome: import("effect/Schema").optional<import("effect/Schema").Literals<readonly ["marked", "superseded"]>>;
|
|
159
185
|
}>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ConnectedAccountNotFoundError | typeof import("@sealant/api-contracts").ConnectedAccountInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
160
186
|
readonly setConnectedAccountDefault: HttpApiClient.Client.Method<import("effect/http-api/HttpApiEndpoint").HttpApiEndpoint<"setConnectedAccountDefault", "PUT", "/v1/connected-accounts/defaults/:provider", import("effect/Schema").toCodecStringTree<import("effect/Schema").Struct<{
|
|
@@ -182,6 +208,12 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
182
208
|
}>;
|
|
183
209
|
readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
184
210
|
readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
|
|
211
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
212
|
+
readonly kind: import("effect/Schema").Literal<"subscription">;
|
|
213
|
+
}>, import("effect/Schema").Struct<{
|
|
214
|
+
readonly kind: import("effect/Schema").Literal<"api-key">;
|
|
215
|
+
readonly suffix: import("effect/Schema").String;
|
|
216
|
+
}>]>>;
|
|
185
217
|
}>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ConnectedAccountNotFoundError | typeof import("@sealant/api-contracts").ConnectedAccountConflictError | typeof import("@sealant/api-contracts").ConnectedAccountInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
186
218
|
};
|
|
187
219
|
readonly github: {
|
|
@@ -381,6 +413,12 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
381
413
|
}>;
|
|
382
414
|
readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
383
415
|
readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
|
|
416
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
417
|
+
readonly kind: import("effect/Schema").Literal<"subscription">;
|
|
418
|
+
}>, import("effect/Schema").Struct<{
|
|
419
|
+
readonly kind: import("effect/Schema").Literal<"api-key">;
|
|
420
|
+
readonly suffix: import("effect/Schema").String;
|
|
421
|
+
}>]>>;
|
|
384
422
|
}>;
|
|
385
423
|
}>>;
|
|
386
424
|
}>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ProfileNotFoundError | typeof import("@sealant/api-contracts").ProfileInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
@@ -430,6 +468,12 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
430
468
|
}>;
|
|
431
469
|
readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
432
470
|
readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
|
|
471
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
472
|
+
readonly kind: import("effect/Schema").Literal<"subscription">;
|
|
473
|
+
}>, import("effect/Schema").Struct<{
|
|
474
|
+
readonly kind: import("effect/Schema").Literal<"api-key">;
|
|
475
|
+
readonly suffix: import("effect/Schema").String;
|
|
476
|
+
}>]>>;
|
|
433
477
|
}>;
|
|
434
478
|
}>>;
|
|
435
479
|
}>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ProfileBadRequestError | typeof import("@sealant/api-contracts").ProfileNotFoundError | typeof import("@sealant/api-contracts").ProfileConflictError | typeof import("@sealant/api-contracts").ProfileInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
@@ -985,6 +1029,7 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
|
|
|
985
1029
|
readonly personBinding: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
986
1030
|
readonly sshKeyRemovalEndsConnections: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
987
1031
|
readonly connectedAccountDefaults: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
1032
|
+
readonly connectedAccountLoginClass: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
988
1033
|
readonly kubernetesPersonLayout: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
989
1034
|
}>>;
|
|
990
1035
|
}>>, never, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
@@ -1895,6 +1940,12 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
1895
1940
|
}>;
|
|
1896
1941
|
readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
1897
1942
|
readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
|
|
1943
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
1944
|
+
readonly kind: import("effect/Schema").Literal<"subscription">;
|
|
1945
|
+
}>, import("effect/Schema").Struct<{
|
|
1946
|
+
readonly kind: import("effect/Schema").Literal<"api-key">;
|
|
1947
|
+
readonly suffix: import("effect/Schema").String;
|
|
1948
|
+
}>]>>;
|
|
1898
1949
|
}>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ConnectedAccountNotFoundError | typeof import("@sealant/api-contracts").ConnectedAccountInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
1899
1950
|
readonly createConnectedAccount: HttpApiClient.Client.Method<import("effect/http-api/HttpApiEndpoint").HttpApiEndpoint<"createConnectedAccount", "POST", "/v1/connected-accounts/", never, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
|
|
1900
1951
|
readonly ownerUserId: import("effect/Schema").String;
|
|
@@ -1902,6 +1953,7 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
1902
1953
|
readonly name: import("effect/Schema").optional<import("effect/Schema").String>;
|
|
1903
1954
|
readonly accountId: import("effect/Schema").optional<import("effect/Schema").String>;
|
|
1904
1955
|
readonly makeDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
1956
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Literals<readonly ["subscription", "api-key"]>>;
|
|
1905
1957
|
readonly secret: import("effect/Schema").String;
|
|
1906
1958
|
}>>, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
|
|
1907
1959
|
readonly connectedAccountId: import("effect/Schema").String;
|
|
@@ -1923,6 +1975,12 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
1923
1975
|
}>;
|
|
1924
1976
|
readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
1925
1977
|
readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
|
|
1978
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
1979
|
+
readonly kind: import("effect/Schema").Literal<"subscription">;
|
|
1980
|
+
}>, import("effect/Schema").Struct<{
|
|
1981
|
+
readonly kind: import("effect/Schema").Literal<"api-key">;
|
|
1982
|
+
readonly suffix: import("effect/Schema").String;
|
|
1983
|
+
}>]>>;
|
|
1926
1984
|
}>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ConnectedAccountBadRequestError | typeof import("@sealant/api-contracts").ConnectedAccountNotFoundError | typeof import("@sealant/api-contracts").ConnectedAccountConflictError | typeof import("@sealant/api-contracts").ConnectedAccountServiceUnavailableError | typeof import("@sealant/api-contracts").ConnectedAccountInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
1927
1985
|
readonly listConnectedAccounts: HttpApiClient.Client.Method<import("effect/http-api/HttpApiEndpoint").HttpApiEndpoint<"listConnectedAccounts", "GET", "/v1/connected-accounts/", never, import("effect/Schema").toCodecStringTree<import("effect/Schema").Struct<{
|
|
1928
1986
|
readonly ownerUserId: import("effect/Schema").String;
|
|
@@ -1947,7 +2005,14 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
1947
2005
|
}>;
|
|
1948
2006
|
readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
1949
2007
|
readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
|
|
1950
|
-
|
|
2008
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
2009
|
+
readonly kind: import("effect/Schema").Literal<"subscription">;
|
|
2010
|
+
}>, import("effect/Schema").Struct<{
|
|
2011
|
+
readonly kind: import("effect/Schema").Literal<"api-key">;
|
|
2012
|
+
readonly suffix: import("effect/Schema").String;
|
|
2013
|
+
}>]>>;
|
|
2014
|
+
}>>;
|
|
2015
|
+
readonly hasApiKeyHistory: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
1951
2016
|
}>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ConnectedAccountInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
1952
2017
|
readonly markConnectedAccountInvalid: HttpApiClient.Client.Method<import("effect/http-api/HttpApiEndpoint").HttpApiEndpoint<"markConnectedAccountInvalid", "POST", "/v1/connected-accounts/:connectedAccountId/mark-invalid", import("effect/Schema").toCodecStringTree<import("effect/Schema").Struct<{
|
|
1953
2018
|
readonly connectedAccountId: import("effect/Schema").String;
|
|
@@ -1974,6 +2039,12 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
1974
2039
|
}>;
|
|
1975
2040
|
readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
1976
2041
|
readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
|
|
2042
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
2043
|
+
readonly kind: import("effect/Schema").Literal<"subscription">;
|
|
2044
|
+
}>, import("effect/Schema").Struct<{
|
|
2045
|
+
readonly kind: import("effect/Schema").Literal<"api-key">;
|
|
2046
|
+
readonly suffix: import("effect/Schema").String;
|
|
2047
|
+
}>]>>;
|
|
1977
2048
|
readonly outcome: import("effect/Schema").optional<import("effect/Schema").Literals<readonly ["marked", "superseded"]>>;
|
|
1978
2049
|
}>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ConnectedAccountNotFoundError | typeof import("@sealant/api-contracts").ConnectedAccountInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
1979
2050
|
readonly setConnectedAccountDefault: HttpApiClient.Client.Method<import("effect/http-api/HttpApiEndpoint").HttpApiEndpoint<"setConnectedAccountDefault", "PUT", "/v1/connected-accounts/defaults/:provider", import("effect/Schema").toCodecStringTree<import("effect/Schema").Struct<{
|
|
@@ -2001,6 +2072,12 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
2001
2072
|
}>;
|
|
2002
2073
|
readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
2003
2074
|
readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
|
|
2075
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
2076
|
+
readonly kind: import("effect/Schema").Literal<"subscription">;
|
|
2077
|
+
}>, import("effect/Schema").Struct<{
|
|
2078
|
+
readonly kind: import("effect/Schema").Literal<"api-key">;
|
|
2079
|
+
readonly suffix: import("effect/Schema").String;
|
|
2080
|
+
}>]>>;
|
|
2004
2081
|
}>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ConnectedAccountNotFoundError | typeof import("@sealant/api-contracts").ConnectedAccountConflictError | typeof import("@sealant/api-contracts").ConnectedAccountInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
2005
2082
|
};
|
|
2006
2083
|
readonly github: {
|
|
@@ -2200,6 +2277,12 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
2200
2277
|
}>;
|
|
2201
2278
|
readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
2202
2279
|
readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
|
|
2280
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
2281
|
+
readonly kind: import("effect/Schema").Literal<"subscription">;
|
|
2282
|
+
}>, import("effect/Schema").Struct<{
|
|
2283
|
+
readonly kind: import("effect/Schema").Literal<"api-key">;
|
|
2284
|
+
readonly suffix: import("effect/Schema").String;
|
|
2285
|
+
}>]>>;
|
|
2203
2286
|
}>;
|
|
2204
2287
|
}>>;
|
|
2205
2288
|
}>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ProfileNotFoundError | typeof import("@sealant/api-contracts").ProfileInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
@@ -2249,6 +2332,12 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
2249
2332
|
}>;
|
|
2250
2333
|
readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
2251
2334
|
readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
|
|
2335
|
+
readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
|
|
2336
|
+
readonly kind: import("effect/Schema").Literal<"subscription">;
|
|
2337
|
+
}>, import("effect/Schema").Struct<{
|
|
2338
|
+
readonly kind: import("effect/Schema").Literal<"api-key">;
|
|
2339
|
+
readonly suffix: import("effect/Schema").String;
|
|
2340
|
+
}>]>>;
|
|
2252
2341
|
}>;
|
|
2253
2342
|
}>>;
|
|
2254
2343
|
}>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ProfileBadRequestError | typeof import("@sealant/api-contracts").ProfileNotFoundError | typeof import("@sealant/api-contracts").ProfileConflictError | typeof import("@sealant/api-contracts").ProfileInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
@@ -2804,6 +2893,7 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
|
|
|
2804
2893
|
readonly personBinding: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
2805
2894
|
readonly sshKeyRemovalEndsConnections: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
2806
2895
|
readonly connectedAccountDefaults: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
2896
|
+
readonly connectedAccountLoginClass: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
2807
2897
|
readonly kubernetesPersonLayout: import("effect/Schema").optional<import("effect/Schema").Boolean>;
|
|
2808
2898
|
}>>;
|
|
2809
2899
|
}>>, never, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
|
|
@@ -1111,7 +1111,14 @@ export declare const listConnectedAccountsOp: (ownerUserId: string) => Effect.Ef
|
|
|
1111
1111
|
};
|
|
1112
1112
|
readonly isDefault?: boolean | undefined;
|
|
1113
1113
|
readonly credentialRevision?: number | undefined;
|
|
1114
|
+
readonly login?: {
|
|
1115
|
+
readonly kind: "subscription";
|
|
1116
|
+
} | {
|
|
1117
|
+
readonly kind: "api-key";
|
|
1118
|
+
readonly suffix: string;
|
|
1119
|
+
} | undefined;
|
|
1114
1120
|
}[];
|
|
1121
|
+
readonly hasApiKeyHistory?: boolean | undefined;
|
|
1115
1122
|
}, import("@sealant/api-contracts").ConnectedAccountInternalServerError | import("effect/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RequestRefusedError | import("effect/Schema").SchemaError, SealantApiClient>;
|
|
1116
1123
|
export declare const createConnectedAccountOp: (payload: {
|
|
1117
1124
|
readonly ownerUserId: string;
|
|
@@ -1119,6 +1126,7 @@ export declare const createConnectedAccountOp: (payload: {
|
|
|
1119
1126
|
readonly name?: string | undefined;
|
|
1120
1127
|
readonly accountId?: string | undefined;
|
|
1121
1128
|
readonly makeDefault?: boolean | undefined;
|
|
1129
|
+
readonly login?: "api-key" | "subscription" | undefined;
|
|
1122
1130
|
readonly secret: string;
|
|
1123
1131
|
}) => Effect.Effect<{
|
|
1124
1132
|
readonly connectedAccountId: string;
|
|
@@ -1142,6 +1150,12 @@ export declare const createConnectedAccountOp: (payload: {
|
|
|
1142
1150
|
};
|
|
1143
1151
|
readonly isDefault?: boolean | undefined;
|
|
1144
1152
|
readonly credentialRevision?: number | undefined;
|
|
1153
|
+
readonly login?: {
|
|
1154
|
+
readonly kind: "subscription";
|
|
1155
|
+
} | {
|
|
1156
|
+
readonly kind: "api-key";
|
|
1157
|
+
readonly suffix: string;
|
|
1158
|
+
} | undefined;
|
|
1145
1159
|
}, 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/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RequestRefusedError | import("effect/Schema").SchemaError, SealantApiClient>;
|
|
1146
1160
|
export declare const archiveConnectedAccountOp: (connectedAccountId: string, ownerUserId: string) => Effect.Effect<{
|
|
1147
1161
|
readonly connectedAccountId: string;
|
|
@@ -1165,6 +1179,12 @@ export declare const archiveConnectedAccountOp: (connectedAccountId: string, own
|
|
|
1165
1179
|
};
|
|
1166
1180
|
readonly isDefault?: boolean | undefined;
|
|
1167
1181
|
readonly credentialRevision?: number | undefined;
|
|
1182
|
+
readonly login?: {
|
|
1183
|
+
readonly kind: "subscription";
|
|
1184
|
+
} | {
|
|
1185
|
+
readonly kind: "api-key";
|
|
1186
|
+
readonly suffix: string;
|
|
1187
|
+
} | undefined;
|
|
1168
1188
|
}, import("@sealant/api-contracts").ConnectedAccountInternalServerError | import("@sealant/api-contracts").ConnectedAccountNotFoundError | import("effect/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RequestRefusedError | import("effect/Schema").SchemaError, SealantApiClient>;
|
|
1169
1189
|
export declare const setConnectedAccountDefaultOp: (provider: "claude" | "codex" | "github", payload: {
|
|
1170
1190
|
readonly ownerUserId: string;
|
|
@@ -1191,6 +1211,12 @@ export declare const setConnectedAccountDefaultOp: (provider: "claude" | "codex"
|
|
|
1191
1211
|
};
|
|
1192
1212
|
readonly isDefault?: boolean | undefined;
|
|
1193
1213
|
readonly credentialRevision?: number | undefined;
|
|
1214
|
+
readonly login?: {
|
|
1215
|
+
readonly kind: "subscription";
|
|
1216
|
+
} | {
|
|
1217
|
+
readonly kind: "api-key";
|
|
1218
|
+
readonly suffix: string;
|
|
1219
|
+
} | undefined;
|
|
1194
1220
|
}, import("@sealant/api-contracts").ConnectedAccountConflictError | import("@sealant/api-contracts").ConnectedAccountInternalServerError | import("@sealant/api-contracts").ConnectedAccountNotFoundError | import("effect/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RequestRefusedError | import("effect/Schema").SchemaError, SealantApiClient>;
|
|
1195
1221
|
export declare const markConnectedAccountInvalidOp: (connectedAccountId: string, payload: {
|
|
1196
1222
|
readonly ownerUserId: string;
|
|
@@ -1217,6 +1243,12 @@ export declare const markConnectedAccountInvalidOp: (connectedAccountId: string,
|
|
|
1217
1243
|
};
|
|
1218
1244
|
readonly isDefault?: boolean | undefined;
|
|
1219
1245
|
readonly credentialRevision?: number | undefined;
|
|
1246
|
+
readonly login?: {
|
|
1247
|
+
readonly kind: "subscription";
|
|
1248
|
+
} | {
|
|
1249
|
+
readonly kind: "api-key";
|
|
1250
|
+
readonly suffix: string;
|
|
1251
|
+
} | undefined;
|
|
1220
1252
|
readonly outcome?: "marked" | "superseded" | undefined;
|
|
1221
1253
|
}, import("@sealant/api-contracts").ConnectedAccountInternalServerError | import("@sealant/api-contracts").ConnectedAccountNotFoundError | import("effect/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RequestRefusedError | import("effect/Schema").SchemaError, SealantApiClient>;
|
|
1222
1254
|
export declare const inferenceRespondOp: (payload: {
|
|
@@ -1280,6 +1312,7 @@ export declare const getIndexOp: () => Effect.Effect<{
|
|
|
1280
1312
|
readonly personBinding?: boolean | undefined;
|
|
1281
1313
|
readonly sshKeyRemovalEndsConnections?: boolean | undefined;
|
|
1282
1314
|
readonly connectedAccountDefaults?: boolean | undefined;
|
|
1315
|
+
readonly connectedAccountLoginClass?: boolean | undefined;
|
|
1283
1316
|
readonly kubernetesPersonLayout?: boolean | undefined;
|
|
1284
1317
|
} | undefined;
|
|
1285
1318
|
}, import("effect/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RequestRefusedError | import("effect/Schema").SchemaError, SealantApiClient>;
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ForwardRefusalReason } from "@sealant/api-contracts";
|
|
2
|
+
import type { ForwardResetCause } from "./types.js";
|
|
2
3
|
/**
|
|
3
4
|
* Public SDK errors. The Effect core fails on a typed `Schema.TaggedError` channel; the facade maps
|
|
4
5
|
* every tagged failure onto one of these PLAIN `Error` subclasses (a single `_tag` switch) so the
|
|
@@ -82,3 +83,11 @@ export declare class SealantNotImplementedError extends SealantError {
|
|
|
82
83
|
readonly name = "SealantNotImplementedError";
|
|
83
84
|
constructor(operation: string);
|
|
84
85
|
}
|
|
86
|
+
/** A bounded forward was reset after it opened. Its streams error with this. */
|
|
87
|
+
export declare class ForwardResetError extends SealantError {
|
|
88
|
+
readonly name: string;
|
|
89
|
+
readonly reason: ForwardResetCause;
|
|
90
|
+
/** The reason Core sent, when this SDK does not know it (`unrecognized-reset`). */
|
|
91
|
+
readonly wireReason?: string;
|
|
92
|
+
constructor(reason: ForwardResetCause, wireReason?: string);
|
|
93
|
+
}
|
package/dist/errors.js
CHANGED
|
@@ -85,3 +85,16 @@ export class SealantNotImplementedError extends SealantError {
|
|
|
85
85
|
super(`${operation} is part of the Sealant SDK surface but is not implemented in this build yet.`, { code: "not_implemented" });
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
+
/** A bounded forward was reset after it opened. Its streams error with this. */
|
|
89
|
+
export class ForwardResetError extends SealantError {
|
|
90
|
+
name = "ForwardResetError";
|
|
91
|
+
reason;
|
|
92
|
+
/** The reason Core sent, when this SDK does not know it (`unrecognized-reset`). */
|
|
93
|
+
wireReason;
|
|
94
|
+
constructor(reason, wireReason) {
|
|
95
|
+
super(`The forward was reset: ${wireReason ?? reason}.`, { code: "ForwardResetError" });
|
|
96
|
+
this.reason = reason;
|
|
97
|
+
if (wireReason !== undefined)
|
|
98
|
+
this.wireReason = wireReason;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BoundedWorkspaceForward } from "../types.js";
|
|
2
|
+
export declare const boundedForward: (ws: WebSocket, binding: {
|
|
3
|
+
readonly kind: "port";
|
|
4
|
+
} | {
|
|
5
|
+
readonly kind: "session";
|
|
6
|
+
readonly session: string;
|
|
7
|
+
}, onWindowOpen: () => void) => BoundedWorkspaceForward;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { CountQueuingStrategy, ReadableStream, WritableStream, } from "node:stream/web";
|
|
2
|
+
import { FORWARD_V2_MAX_FRAME_BYTES, ForwardFlow, forwardFrames, forwardResetReasonSchema, } from "@sealant/api-contracts";
|
|
3
|
+
import { Schema } from "effect";
|
|
4
|
+
import { ForwardResetError } from "../errors.js";
|
|
5
|
+
const isKnownReset = Schema.is(forwardResetReasonSchema);
|
|
6
|
+
const SOCKET_HIGH_WATER_BYTES = FORWARD_V2_MAX_FRAME_BYTES;
|
|
7
|
+
/** A WHATWG socket has no drain event, so a held `progress` checks again this often. */
|
|
8
|
+
const HELD_PROGRESS_RETRY_MS = 10;
|
|
9
|
+
export const boundedForward = (ws, binding, onWindowOpen) => {
|
|
10
|
+
const flow = new ForwardFlow();
|
|
11
|
+
const unread = [];
|
|
12
|
+
const closed = Promise.withResolvers();
|
|
13
|
+
let state = { kind: "open" };
|
|
14
|
+
let windowOpen = false;
|
|
15
|
+
let finReceived = false;
|
|
16
|
+
let reader;
|
|
17
|
+
let writer;
|
|
18
|
+
let pendingRead;
|
|
19
|
+
let creditWaiter;
|
|
20
|
+
let progressRetry;
|
|
21
|
+
const sendText = (frame) => ws.send(JSON.stringify(frame));
|
|
22
|
+
const wakeReader = () => {
|
|
23
|
+
const waiter = pendingRead;
|
|
24
|
+
pendingRead = undefined;
|
|
25
|
+
waiter?.();
|
|
26
|
+
};
|
|
27
|
+
const wakeWriter = () => {
|
|
28
|
+
const waiter = creditWaiter;
|
|
29
|
+
creditWaiter = undefined;
|
|
30
|
+
waiter?.();
|
|
31
|
+
};
|
|
32
|
+
const settle = (next, close) => {
|
|
33
|
+
if (state.kind !== "open")
|
|
34
|
+
return;
|
|
35
|
+
state = next;
|
|
36
|
+
clearTimeout(progressRetry);
|
|
37
|
+
closed.resolve(close);
|
|
38
|
+
if (next.kind === "failed") {
|
|
39
|
+
writer?.error(next.error);
|
|
40
|
+
if (next.readable === "errored")
|
|
41
|
+
reader?.error(next.error);
|
|
42
|
+
}
|
|
43
|
+
wakeReader();
|
|
44
|
+
wakeWriter();
|
|
45
|
+
};
|
|
46
|
+
const resetBy = (reason, wireReason) => settle({ kind: "failed", error: new ForwardResetError(reason, wireReason), readable: "errored" }, { kind: "reset", reason, ...(wireReason === undefined ? {} : { wireReason }) });
|
|
47
|
+
const reset = (reason) => {
|
|
48
|
+
if (state.kind !== "open")
|
|
49
|
+
return;
|
|
50
|
+
if (ws.readyState === WebSocket.OPEN)
|
|
51
|
+
sendText({ t: "reset", reason });
|
|
52
|
+
resetBy(reason);
|
|
53
|
+
ws.close();
|
|
54
|
+
};
|
|
55
|
+
const endedByWorkspace = () => {
|
|
56
|
+
if (flow.delivered && creditWaiter === undefined) {
|
|
57
|
+
return settle({ kind: "ended" }, { kind: "end" });
|
|
58
|
+
}
|
|
59
|
+
settle({
|
|
60
|
+
kind: "failed",
|
|
61
|
+
error: new ForwardResetError("workspace-ended"),
|
|
62
|
+
readable: "drains-what-arrived",
|
|
63
|
+
}, { kind: "reset", reason: "workspace-ended" });
|
|
64
|
+
};
|
|
65
|
+
const sendProgress = () => {
|
|
66
|
+
if (state.kind !== "open" || ws.readyState !== WebSocket.OPEN || !flow.progressDue)
|
|
67
|
+
return;
|
|
68
|
+
if (ws.bufferedAmount > SOCKET_HIGH_WATER_BYTES) {
|
|
69
|
+
progressRetry ??= setTimeout(() => {
|
|
70
|
+
progressRetry = undefined;
|
|
71
|
+
sendProgress();
|
|
72
|
+
}, HELD_PROGRESS_RETRY_MS);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const progress = flow.takeProgress();
|
|
76
|
+
if (progress !== undefined)
|
|
77
|
+
sendText(progress);
|
|
78
|
+
};
|
|
79
|
+
const deliver = (controller, chunk) => {
|
|
80
|
+
controller.enqueue(chunk);
|
|
81
|
+
flow.consumed(chunk.byteLength);
|
|
82
|
+
sendProgress();
|
|
83
|
+
};
|
|
84
|
+
const onInbound = (frame) => {
|
|
85
|
+
const inbound = flow.receive(frame);
|
|
86
|
+
switch (inbound.kind) {
|
|
87
|
+
case "data":
|
|
88
|
+
if (pendingRead === undefined || reader === undefined) {
|
|
89
|
+
unread.push(inbound.data);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
deliver(reader, inbound.data);
|
|
93
|
+
return wakeReader();
|
|
94
|
+
case "progress":
|
|
95
|
+
if (!windowOpen) {
|
|
96
|
+
windowOpen = true;
|
|
97
|
+
onWindowOpen();
|
|
98
|
+
}
|
|
99
|
+
return wakeWriter();
|
|
100
|
+
case "fin":
|
|
101
|
+
finReceived = true;
|
|
102
|
+
if (unread.length === 0)
|
|
103
|
+
reader?.close();
|
|
104
|
+
wakeReader();
|
|
105
|
+
return endedByWorkspace();
|
|
106
|
+
case "reset": {
|
|
107
|
+
const known = isKnownReset(inbound.reason);
|
|
108
|
+
resetBy(known ? inbound.reason : "unrecognized-reset", known ? undefined : inbound.reason);
|
|
109
|
+
return ws.close();
|
|
110
|
+
}
|
|
111
|
+
case "violation":
|
|
112
|
+
return reset(inbound.reason);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
ws.addEventListener("message", (event) => {
|
|
116
|
+
if (state.kind !== "open")
|
|
117
|
+
return;
|
|
118
|
+
if (event.data instanceof ArrayBuffer)
|
|
119
|
+
onInbound(new Uint8Array(event.data));
|
|
120
|
+
else if (typeof event.data === "string")
|
|
121
|
+
onInbound(event.data);
|
|
122
|
+
});
|
|
123
|
+
ws.addEventListener("close", () => resetBy("connection-lost"));
|
|
124
|
+
const readable = new ReadableStream({
|
|
125
|
+
start: (controller) => {
|
|
126
|
+
reader = controller;
|
|
127
|
+
},
|
|
128
|
+
pull: (controller) => {
|
|
129
|
+
const chunk = unread.shift();
|
|
130
|
+
if (chunk === undefined) {
|
|
131
|
+
return new Promise((resolve) => {
|
|
132
|
+
pendingRead = resolve;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
deliver(controller, chunk);
|
|
136
|
+
if (finReceived && unread.length === 0)
|
|
137
|
+
controller.close();
|
|
138
|
+
},
|
|
139
|
+
cancel: () => reset("aborted"),
|
|
140
|
+
}, { highWaterMark: 0 });
|
|
141
|
+
const waitForCredit = (fits) => new Promise((resolve, reject) => {
|
|
142
|
+
const check = () => {
|
|
143
|
+
if (state.kind === "failed")
|
|
144
|
+
return reject(state.error);
|
|
145
|
+
if (state.kind === "ended")
|
|
146
|
+
return reject(new ForwardResetError("workspace-ended"));
|
|
147
|
+
if (fits())
|
|
148
|
+
return resolve();
|
|
149
|
+
creditWaiter = check;
|
|
150
|
+
};
|
|
151
|
+
check();
|
|
152
|
+
});
|
|
153
|
+
const holdReadyUntilCredit = () => waitForCredit(() => flow.canSend(1));
|
|
154
|
+
const writable = new WritableStream({
|
|
155
|
+
start: (controller) => {
|
|
156
|
+
writer = controller;
|
|
157
|
+
// The sink's own `abort` runs only after a write waiting for credit settles. Node has had
|
|
158
|
+
// `signal` since 18; its types still lack it.
|
|
159
|
+
const signal = "signal" in controller ? controller.signal : undefined;
|
|
160
|
+
if (!(signal instanceof AbortSignal))
|
|
161
|
+
return;
|
|
162
|
+
signal.addEventListener("abort", () => {
|
|
163
|
+
// The stream is already erroring: erroring it again from here breaks Node's abort.
|
|
164
|
+
writer = undefined;
|
|
165
|
+
reset("aborted");
|
|
166
|
+
});
|
|
167
|
+
},
|
|
168
|
+
write: async (chunk) => {
|
|
169
|
+
for (const frame of forwardFrames(chunk)) {
|
|
170
|
+
await waitForCredit(() => flow.canSend(frame.byteLength));
|
|
171
|
+
flow.sent(frame.byteLength);
|
|
172
|
+
// A copy: a WHATWG socket refuses a view of a SharedArrayBuffer.
|
|
173
|
+
ws.send(new Uint8Array(frame));
|
|
174
|
+
}
|
|
175
|
+
await holdReadyUntilCredit();
|
|
176
|
+
},
|
|
177
|
+
close: () => {
|
|
178
|
+
if (state.kind === "open")
|
|
179
|
+
sendText(flow.fin());
|
|
180
|
+
},
|
|
181
|
+
abort: () => reset("aborted"),
|
|
182
|
+
}, new CountQueuingStrategy({ highWaterMark: 1 }));
|
|
183
|
+
sendText(flow.open());
|
|
184
|
+
return {
|
|
185
|
+
readable,
|
|
186
|
+
writable,
|
|
187
|
+
binding,
|
|
188
|
+
closed: closed.promise,
|
|
189
|
+
close: () => reset("aborted"),
|
|
190
|
+
};
|
|
191
|
+
};
|
package/dist/facade/forward.d.ts
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { WorkspaceForward, WorkspaceForwardOptions } from "../types.js";
|
|
1
|
+
import type { BoundedForwardTarget, BoundedWorkspaceForward, BoundedWorkspaceForwardOptions, WorkspaceForward, WorkspaceForwardOptions } from "../types.js";
|
|
3
2
|
export interface ForwardEndpoint {
|
|
4
3
|
readonly baseUrl: string;
|
|
5
4
|
readonly apiKey: string | undefined;
|
|
6
5
|
readonly ownerUserId: string;
|
|
7
6
|
}
|
|
8
7
|
export declare const openForward: (endpoint: ForwardEndpoint, workspaceId: string, port: number, options?: WorkspaceForwardOptions | undefined) => Promise<WorkspaceForward>;
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
readonly signal?: AbortSignal;
|
|
12
|
-
readonly deadlineMs?: number;
|
|
13
|
-
}
|
|
14
|
-
export interface OpenedForwardV2 {
|
|
15
|
-
readonly binding: ForwardBinding;
|
|
16
|
-
readonly forward: WorkspaceForward;
|
|
17
|
-
}
|
|
18
|
-
export declare const openForwardV2: (endpoint: ForwardEndpoint, workspaceId: string, port: number, options?: ForwardV2Options | undefined) => Promise<OpenedForwardV2>;
|
|
8
|
+
export type ForwardV2Options = BoundedForwardTarget & Pick<BoundedWorkspaceForwardOptions, "signal" | "deadlineMs">;
|
|
9
|
+
export declare const openForwardV2: (endpoint: ForwardEndpoint, workspaceId: string, port: number, options?: ForwardV2Options | undefined) => Promise<BoundedWorkspaceForward>;
|
package/dist/facade/forward.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FORWARD_REFUSAL_STATUS, FORWARD_V2_SUBPROTOCOL, forwardOpenFrameSchema, forwardRefusalReasonSchema, } from "@sealant/api-contracts";
|
|
2
2
|
import { Option, Schema } from "effect";
|
|
3
3
|
import { ForwardDialError } from "../errors.js";
|
|
4
|
+
import { boundedForward } from "./bounded-forward.js";
|
|
4
5
|
const forwardUrl = (endpoint, path, query) => {
|
|
5
6
|
const url = new URL(path, endpoint.baseUrl);
|
|
6
7
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
@@ -16,7 +17,7 @@ const forwardUrl = (endpoint, path, query) => {
|
|
|
16
17
|
}
|
|
17
18
|
return url;
|
|
18
19
|
};
|
|
19
|
-
const bridgeForward = (ws
|
|
20
|
+
const bridgeForward = (ws) => {
|
|
20
21
|
const pending = [];
|
|
21
22
|
let wake;
|
|
22
23
|
let finished = false;
|
|
@@ -30,7 +31,7 @@ const bridgeForward = (ws, maxFrameBytes = Number.POSITIVE_INFINITY) => {
|
|
|
30
31
|
wake?.();
|
|
31
32
|
};
|
|
32
33
|
ws.addEventListener("close", () => finish("closed"));
|
|
33
|
-
|
|
34
|
+
ws.addEventListener("message", (event) => {
|
|
34
35
|
if (typeof event.data === "string") {
|
|
35
36
|
try {
|
|
36
37
|
const frame = JSON.parse(event.data);
|
|
@@ -43,7 +44,7 @@ const bridgeForward = (ws, maxFrameBytes = Number.POSITIVE_INFINITY) => {
|
|
|
43
44
|
}
|
|
44
45
|
pending.push(new Uint8Array(event.data));
|
|
45
46
|
wake?.();
|
|
46
|
-
};
|
|
47
|
+
});
|
|
47
48
|
const output = {
|
|
48
49
|
[Symbol.asyncIterator]: () => ({
|
|
49
50
|
next: async () => {
|
|
@@ -63,14 +64,10 @@ const bridgeForward = (ws, maxFrameBytes = Number.POSITIVE_INFINITY) => {
|
|
|
63
64
|
},
|
|
64
65
|
}),
|
|
65
66
|
};
|
|
66
|
-
|
|
67
|
+
return {
|
|
67
68
|
send: (input) => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// Copy into a plain ArrayBuffer-backed view (WebSocket.send rejects SharedArrayBuffer views).
|
|
71
|
-
ws.send(new Uint8Array(input.subarray(offset, offset + maxFrameBytes)).buffer);
|
|
72
|
-
offset += maxFrameBytes;
|
|
73
|
-
} while (offset < input.byteLength);
|
|
69
|
+
// Copy into a plain ArrayBuffer-backed view (WebSocket.send rejects SharedArrayBuffer views).
|
|
70
|
+
ws.send(new Uint8Array(input).buffer);
|
|
74
71
|
},
|
|
75
72
|
eof: () => {
|
|
76
73
|
ws.send(JSON.stringify({ t: "eof" }));
|
|
@@ -82,7 +79,6 @@ const bridgeForward = (ws, maxFrameBytes = Number.POSITIVE_INFINITY) => {
|
|
|
82
79
|
ws.close();
|
|
83
80
|
},
|
|
84
81
|
};
|
|
85
|
-
return { forward, receive };
|
|
86
82
|
};
|
|
87
83
|
export const openForward = (endpoint, workspaceId, port, options) => {
|
|
88
84
|
const url = forwardUrl(endpoint, `/v1/workspaces/${workspaceId}/forward`, {
|
|
@@ -93,8 +89,7 @@ export const openForward = (endpoint, workspaceId, port, options) => {
|
|
|
93
89
|
return new Promise((resolve, reject) => {
|
|
94
90
|
const ws = new WebSocket(url);
|
|
95
91
|
ws.binaryType = "arraybuffer";
|
|
96
|
-
const
|
|
97
|
-
ws.addEventListener("message", receive);
|
|
92
|
+
const forward = bridgeForward(ws);
|
|
98
93
|
ws.addEventListener("open", () => resolve(forward), { once: true });
|
|
99
94
|
ws.addEventListener("error", () => reject(new Error(`workspace forward failed: could not connect to ${url.host} (is anything listening on 127.0.0.1:${port} in the workspace?)`)), { once: true });
|
|
100
95
|
});
|
|
@@ -136,11 +131,13 @@ const failedHandshakeError = async (url, origin, signal) => {
|
|
|
136
131
|
};
|
|
137
132
|
export const openForwardV2 = (endpoint, workspaceId, port, options) => {
|
|
138
133
|
const host = options?.host ?? "127.0.0.1";
|
|
134
|
+
const session = options?.session;
|
|
139
135
|
const signal = options?.signal;
|
|
140
136
|
const deadlineMs = options?.deadlineMs ?? FORWARD_V2_OPEN_DEADLINE_MS;
|
|
141
137
|
const url = forwardUrl(endpoint, `/v1/workspaces/${workspaceId}/forward/v2`, {
|
|
142
138
|
port: String(port),
|
|
143
139
|
host,
|
|
140
|
+
session,
|
|
144
141
|
});
|
|
145
142
|
const origin = new URL(endpoint.baseUrl).origin;
|
|
146
143
|
return new Promise((resolve, reject) => {
|
|
@@ -148,7 +145,6 @@ export const openForwardV2 = (endpoint, workspaceId, port, options) => {
|
|
|
148
145
|
return reject(signal.reason);
|
|
149
146
|
const ws = new WebSocket(url, FORWARD_V2_SUBPROTOCOL);
|
|
150
147
|
ws.binaryType = "arraybuffer";
|
|
151
|
-
const { forward, receive } = bridgeForward(ws, FORWARD_V2_MAX_FRAME_BYTES);
|
|
152
148
|
const probing = new AbortController();
|
|
153
149
|
let phase = "handshake";
|
|
154
150
|
const settle = (next) => {
|
|
@@ -178,8 +174,6 @@ export const openForwardV2 = (endpoint, workspaceId, port, options) => {
|
|
|
178
174
|
}
|
|
179
175
|
});
|
|
180
176
|
ws.addEventListener("message", (event) => {
|
|
181
|
-
if (phase === "open")
|
|
182
|
-
return receive(event);
|
|
183
177
|
if (phase !== "awaiting-answer")
|
|
184
178
|
return;
|
|
185
179
|
const frame = typeof event.data === "string" ? decodeOpenFrame(event.data) : Option.none();
|
|
@@ -188,11 +182,22 @@ export const openForwardV2 = (endpoint, workspaceId, port, options) => {
|
|
|
188
182
|
}
|
|
189
183
|
if (frame.value.t === "refused")
|
|
190
184
|
return fail(refusalError(frame.value));
|
|
191
|
-
|
|
192
|
-
|
|
185
|
+
const { binding } = frame.value;
|
|
186
|
+
if (session !== undefined && (binding.kind !== "session" || binding.session !== session)) {
|
|
187
|
+
return fail(new ForwardDialError({
|
|
188
|
+
reason: "binding-unsupported",
|
|
189
|
+
status: FORWARD_REFUSAL_STATUS["binding-unsupported"],
|
|
190
|
+
message: `${origin} opened the forward without binding it to session ${session}.`,
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
phase = "awaiting-window";
|
|
194
|
+
const forward = boundedForward(ws, binding, () => {
|
|
195
|
+
settle("open");
|
|
196
|
+
resolve(forward);
|
|
197
|
+
});
|
|
193
198
|
});
|
|
194
199
|
ws.addEventListener("close", () => {
|
|
195
|
-
if (phase === "awaiting-answer") {
|
|
200
|
+
if (phase === "awaiting-answer" || phase === "awaiting-window") {
|
|
196
201
|
return fail(new ForwardDialError({
|
|
197
202
|
reason: "transport-unavailable",
|
|
198
203
|
message: `The forward's connection to ${origin} closed before the control plane answered.`,
|
package/dist/facade/workspace.js
CHANGED
|
@@ -7,7 +7,7 @@ import { elapsed } from "../internal/clock.js";
|
|
|
7
7
|
import { mapAccountRef } from "../internal/credentials.js";
|
|
8
8
|
import { parseTtlSeconds } from "../internal/duration.js";
|
|
9
9
|
import { requireProcessUser } from "../internal/process-user.js";
|
|
10
|
-
import { openForward } from "./forward.js";
|
|
10
|
+
import { openForward, openForwardV2 } from "./forward.js";
|
|
11
11
|
import { makeInteractiveSession } from "./session.js";
|
|
12
12
|
/** The wire runtime as the SDK reports it. */
|
|
13
13
|
export const toRuntimeInfo = (runtime) => ({
|
|
@@ -295,6 +295,22 @@ const olderControlPlaneRefusal = (argv, error) => {
|
|
|
295
295
|
});
|
|
296
296
|
};
|
|
297
297
|
export const makeWorkspace = (ctx, init) => {
|
|
298
|
+
const endpoint = {
|
|
299
|
+
baseUrl: ctx.config.baseUrl,
|
|
300
|
+
apiKey: ctx.config.apiKey,
|
|
301
|
+
ownerUserId: ctx.config.hostLocal.ownerUserId,
|
|
302
|
+
};
|
|
303
|
+
function forward(port, options) {
|
|
304
|
+
if (options?.flow === "bounded") {
|
|
305
|
+
return openForwardV2(endpoint, init.id, port, options);
|
|
306
|
+
}
|
|
307
|
+
if (options !== undefined && "session" in options) {
|
|
308
|
+
return Promise.reject(new SealantError('A forward bound to a session needs flow: "bounded".', {
|
|
309
|
+
code: "invalid_options",
|
|
310
|
+
}));
|
|
311
|
+
}
|
|
312
|
+
return openForward(endpoint, init.id, port, options);
|
|
313
|
+
}
|
|
298
314
|
const openSession = async (argv, options) => {
|
|
299
315
|
// The contract's own rule, checked before anything is asked, with the same value-free reason.
|
|
300
316
|
const refused = sessionArgvIssue(argv);
|
|
@@ -801,11 +817,7 @@ export const makeWorkspace = (ctx, init) => {
|
|
|
801
817
|
sshAsRoot: async () => {
|
|
802
818
|
await ctx.runtime.run(clearWorkspaceSshUserOp(init.id, { ownerUserId: ctx.config.hostLocal.ownerUserId }));
|
|
803
819
|
},
|
|
804
|
-
forward
|
|
805
|
-
baseUrl: ctx.config.baseUrl,
|
|
806
|
-
apiKey: ctx.config.apiKey,
|
|
807
|
-
ownerUserId: ctx.config.hostLocal.ownerUserId,
|
|
808
|
-
}, init.id, port, options),
|
|
820
|
+
forward,
|
|
809
821
|
};
|
|
810
822
|
return workspace;
|
|
811
823
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
export { Sealant } from "./client.js";
|
|
18
18
|
export { claudeCode, codex, customHarness, opencode, pi } from "./harness.js";
|
|
19
|
-
export { ForwardDialError, type ForwardDialFailure, type ForwardDialReason, SealantApiError, SealantError, SealantNotImplementedError, SealantRuntimeError, } from "./errors.js";
|
|
19
|
+
export { ForwardDialError, type ForwardDialFailure, type ForwardDialReason, ForwardResetError, SealantApiError, SealantError, SealantNotImplementedError, SealantRuntimeError, } from "./errors.js";
|
|
20
20
|
export type * from "./types.js";
|
|
21
21
|
export { findWorkspaceEnvReservedRule, findWorkspaceSecretEnvReservedRule, formatWorkspaceEnvIssue, parseWorkspaceEnv, parseWorkspaceSecretEnv, WORKSPACE_ENV_MAX_ENTRIES, WORKSPACE_ENV_MAX_NAME_LENGTH, WORKSPACE_ENV_MAX_TOTAL_BYTES, WORKSPACE_ENV_MAX_VALUE_BYTES, WORKSPACE_ENV_NAME_PATTERN, WORKSPACE_ENV_SECRET_MARKERS, } from "@sealant/api-contracts/workspace-environment";
|
|
22
22
|
export type { WorkspaceEnvIssue, WorkspaceEnvParseResult, WorkspaceEnvReservedRule, } from "@sealant/api-contracts/workspace-environment";
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
export { Sealant } from "./client.js";
|
|
18
18
|
export { claudeCode, codex, customHarness, opencode, pi } from "./harness.js";
|
|
19
|
-
export { ForwardDialError, SealantApiError, SealantError, SealantNotImplementedError, SealantRuntimeError, } from "./errors.js";
|
|
19
|
+
export { ForwardDialError, ForwardResetError, SealantApiError, SealantError, SealantNotImplementedError, SealantRuntimeError, } from "./errors.js";
|
|
20
20
|
// The workspace environment policy (`CreateOptions.env` validation) is public API: downstream
|
|
21
21
|
// products validate at their own boundaries with the exact rules the platform enforces.
|
|
22
22
|
export { findWorkspaceEnvReservedRule, findWorkspaceSecretEnvReservedRule, formatWorkspaceEnvIssue, parseWorkspaceEnv, parseWorkspaceSecretEnv, WORKSPACE_ENV_MAX_ENTRIES, WORKSPACE_ENV_MAX_NAME_LENGTH, WORKSPACE_ENV_MAX_TOTAL_BYTES, WORKSPACE_ENV_MAX_VALUE_BYTES, WORKSPACE_ENV_NAME_PATTERN, WORKSPACE_ENV_SECRET_MARKERS, } from "@sealant/api-contracts/workspace-environment";
|
|
@@ -17,6 +17,7 @@ export declare const toFeatures: (index: {
|
|
|
17
17
|
readonly personBinding?: boolean | undefined;
|
|
18
18
|
readonly sshKeyRemovalEndsConnections?: boolean | undefined;
|
|
19
19
|
readonly connectedAccountDefaults?: boolean | undefined;
|
|
20
|
+
readonly connectedAccountLoginClass?: boolean | undefined;
|
|
20
21
|
readonly kubernetesPersonLayout?: boolean | undefined;
|
|
21
22
|
} | undefined;
|
|
22
23
|
}) => SealantFeatures;
|
|
@@ -26,3 +27,4 @@ export declare const toFeatures: (index: {
|
|
|
26
27
|
*/
|
|
27
28
|
export declare const readFeatures: (ctx: SdkContext) => Promise<SealantFeatures>;
|
|
28
29
|
export declare const requireConnectedAccountDefaults: (ctx: SdkContext) => Promise<void>;
|
|
30
|
+
export declare const requireConnectedAccountLoginClass: (ctx: SdkContext) => Promise<void>;
|
|
@@ -21,6 +21,7 @@ export const toFeatures = (index) => {
|
|
|
21
21
|
personBinding: features?.personBinding === true,
|
|
22
22
|
sshKeyRemovalEndsConnections: features?.sshKeyRemovalEndsConnections === true,
|
|
23
23
|
connectedAccountDefaults: features?.connectedAccountDefaults === true,
|
|
24
|
+
connectedAccountLoginClass: features?.connectedAccountLoginClass === true,
|
|
24
25
|
kubernetesPersonLayout: features?.kubernetesPersonLayout === true,
|
|
25
26
|
};
|
|
26
27
|
};
|
|
@@ -50,3 +51,9 @@ export const requireConnectedAccountDefaults = async (ctx) => {
|
|
|
50
51
|
throw new SealantError("This control plane cannot choose a default connected account or target one by id (it does not report the feature, or could not be asked); nothing was sent.", { code: "connected-account-defaults-unsupported" });
|
|
51
52
|
}
|
|
52
53
|
};
|
|
54
|
+
export const requireConnectedAccountLoginClass = async (ctx) => {
|
|
55
|
+
const supported = await readFeatures(ctx).then((features) => features.connectedAccountLoginClass, () => false);
|
|
56
|
+
if (!supported) {
|
|
57
|
+
throw new SealantError("This control plane cannot check a connect's login class (it does not report the feature, or could not be asked), and would store the secret whatever it is; nothing was sent.", { code: "connected-account-login-class-unsupported" });
|
|
58
|
+
}
|
|
59
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
* compile against a stable contract from day one (unimplemented paths reject with
|
|
15
15
|
* `SealantNotImplementedError` at runtime, see `./errors.js`).
|
|
16
16
|
*/
|
|
17
|
+
import type { ReadableStream as NodeReadableStream, WritableStream as NodeWritableStream } from "node:stream/web";
|
|
18
|
+
import type { ConnectedAccountLogin, ConnectedAccountLoginClass, ForwardBinding, ForwardResetReason } from "@sealant/api-contracts";
|
|
17
19
|
/**
|
|
18
20
|
* Public client configuration. Intentionally minimal: a base URL and an API key. Host-local
|
|
19
21
|
* concerns required by the current slice (owner identity, registry, direct database access) live in
|
|
@@ -1160,7 +1162,13 @@ export interface Workspace {
|
|
|
1160
1162
|
* workspace-private set: the container's loopback (default), or `docker` —
|
|
1161
1163
|
* the workspace-scoped Docker sidecar's alias, where `docker compose`
|
|
1162
1164
|
* publishes its ports.
|
|
1165
|
+
*
|
|
1166
|
+
* With `flow: "bounded"`, a TCP forward whose two directions are bounded: the workspace's
|
|
1167
|
+
* producer is held back while `readable` is not read, and `writable` holds back its writer while
|
|
1168
|
+
* the workspace does not take the bytes. It resolves once Core and the workspace runtime have
|
|
1169
|
+
* opened it, and rejects with a {@link ForwardDialError} that says why when they did not.
|
|
1163
1170
|
*/
|
|
1171
|
+
forward(port: number, options: BoundedWorkspaceForwardOptions): Promise<BoundedWorkspaceForward>;
|
|
1164
1172
|
forward(port: number, options?: WorkspaceForwardOptions): Promise<WorkspaceForward>;
|
|
1165
1173
|
}
|
|
1166
1174
|
/**
|
|
@@ -1178,7 +1186,7 @@ export interface WorkspaceCredentials {
|
|
|
1178
1186
|
* (missing, not a directory, reached through a symbolic link, or a login directory linking out
|
|
1179
1187
|
* of it), `home-busy` (another write into it is still running: retry), `workspace-not-running`,
|
|
1180
1188
|
* `connected-account-invalid`, `connected-account-unsupported` (a Codex account named for pi or
|
|
1181
|
-
* opencode that is not a ChatGPT login); with
|
|
1189
|
+
* opencode that is not a ChatGPT login, or a Claude API key, which cannot be placed yet); with
|
|
1182
1190
|
* `WorkspaceNotFoundError` for an account `onBehalfOf` cannot name; with
|
|
1183
1191
|
* `WorkspaceBadGatewayError` when the executor did not confirm the write. The body code is
|
|
1184
1192
|
* `SealantApiError.reason`; a refused account is `reason` `connected-account-missing` (404) or
|
|
@@ -1405,6 +1413,12 @@ export interface SealantFeatures {
|
|
|
1405
1413
|
* `default`.
|
|
1406
1414
|
*/
|
|
1407
1415
|
readonly connectedAccountDefaults: boolean;
|
|
1416
|
+
/**
|
|
1417
|
+
* `login` on `connect()` (a secret of the other login class is refused), Claude and Codex API
|
|
1418
|
+
* keys beside a subscription, `ConnectedAccount.login`, and `hasApiKeyHistory` from
|
|
1419
|
+
* `connectedAccounts.listWithHistory()`.
|
|
1420
|
+
*/
|
|
1421
|
+
readonly connectedAccountLoginClass: boolean;
|
|
1408
1422
|
/**
|
|
1409
1423
|
* `ownerMap` is taken on Kubernetes (runc): each person's `sudo` works in the Pod, and images the
|
|
1410
1424
|
* cluster builds record the probe a per-person launch checks. From an older control plane a
|
|
@@ -1442,6 +1456,10 @@ export interface WorkspaceCredentialsHomeOptions {
|
|
|
1442
1456
|
}
|
|
1443
1457
|
/** Options for {@link Workspace.forward}. */
|
|
1444
1458
|
export interface WorkspaceForwardOptions {
|
|
1459
|
+
/** Only a bounded forward has a `flow`. */
|
|
1460
|
+
readonly flow?: never;
|
|
1461
|
+
/** Only a bounded forward can be bound to a session. */
|
|
1462
|
+
readonly session?: never;
|
|
1445
1463
|
/** Target inside the workspace: its loopback (default) or the Docker sidecar. */
|
|
1446
1464
|
readonly host?: "127.0.0.1" | "localhost" | "docker";
|
|
1447
1465
|
/**
|
|
@@ -1469,6 +1487,64 @@ export interface WorkspaceForward {
|
|
|
1469
1487
|
/** Tear the forward down. */
|
|
1470
1488
|
close(): void;
|
|
1471
1489
|
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Where a bounded forward goes: the port on the workspace's loopback (default) or on the Docker
|
|
1492
|
+
* sidecar, whatever holds it; or, with `session`, the port on the loopback only when a process of
|
|
1493
|
+
* that session holds it. A bound forward resets with `owner-exited` once the session's leader exits.
|
|
1494
|
+
*/
|
|
1495
|
+
export type BoundedForwardTarget = {
|
|
1496
|
+
readonly host?: "127.0.0.1" | "localhost" | "docker";
|
|
1497
|
+
readonly session?: never;
|
|
1498
|
+
} | {
|
|
1499
|
+
readonly host?: "127.0.0.1" | "localhost";
|
|
1500
|
+
readonly session: string;
|
|
1501
|
+
};
|
|
1502
|
+
/** Options for a bounded {@link Workspace.forward}. TCP only. */
|
|
1503
|
+
export type BoundedWorkspaceForwardOptions = BoundedForwardTarget & {
|
|
1504
|
+
readonly flow: "bounded";
|
|
1505
|
+
/** Gives up opening the forward; an open forward ignores it. */
|
|
1506
|
+
readonly signal?: AbortSignal;
|
|
1507
|
+
/** How long opening may take before it rejects, in milliseconds. 30 000 by default. */
|
|
1508
|
+
readonly deadlineMs?: number;
|
|
1509
|
+
};
|
|
1510
|
+
/**
|
|
1511
|
+
* Why a bounded forward was reset: a reason either side sent, `connection-lost` when the socket
|
|
1512
|
+
* closed with neither a `fin` nor a reset, `unrecognized-reset` when Core sent a reason newer than
|
|
1513
|
+
* this SDK (kept in `wireReason`), or `workspace-ended` when the workspace ended the forward before
|
|
1514
|
+
* it took everything written to it. A write after the workspace ended fails with `workspace-ended`
|
|
1515
|
+
* too.
|
|
1516
|
+
*/
|
|
1517
|
+
export type ForwardResetCause = ForwardResetReason | "connection-lost" | "unrecognized-reset" | "workspace-ended";
|
|
1518
|
+
/**
|
|
1519
|
+
* How a bounded forward ended: the workspace closed its side after it took everything written to
|
|
1520
|
+
* it (`end`), or the forward was reset.
|
|
1521
|
+
*/
|
|
1522
|
+
export type BoundedWorkspaceForwardClose = {
|
|
1523
|
+
readonly kind: "end";
|
|
1524
|
+
} | {
|
|
1525
|
+
readonly kind: "reset";
|
|
1526
|
+
readonly reason: ForwardResetCause;
|
|
1527
|
+
readonly wireReason?: string;
|
|
1528
|
+
};
|
|
1529
|
+
/**
|
|
1530
|
+
* A bounded forward. Reading `readable` is what lets the workspace send more; `writer.ready` waits
|
|
1531
|
+
* while the workspace can take no more; `writable.close()` half-closes toward the workspace. Wrap
|
|
1532
|
+
* it as a Node stream with `Duplex.fromWeb(forward)`. The streams are typed as `node:stream/web`'s,
|
|
1533
|
+
* which `Duplex.fromWeb` accepts also where the DOM's stream types are loaded.
|
|
1534
|
+
*
|
|
1535
|
+
* A reset fails both streams with a `ForwardResetError`, at once: data the workspace sent that was
|
|
1536
|
+
* not read yet is dropped. When the workspace ends first, `readable` still yields all it sent, and
|
|
1537
|
+
* the writer fails with `workspace-ended` only if something written did not reach the workspace.
|
|
1538
|
+
*/
|
|
1539
|
+
export interface BoundedWorkspaceForward {
|
|
1540
|
+
readonly readable: NodeReadableStream<Uint8Array>;
|
|
1541
|
+
readonly writable: NodeWritableStream<Uint8Array>;
|
|
1542
|
+
/** What the forward is bound to. */
|
|
1543
|
+
readonly binding: ForwardBinding;
|
|
1544
|
+
readonly closed: Promise<BoundedWorkspaceForwardClose>;
|
|
1545
|
+
/** Resets both directions. */
|
|
1546
|
+
close(): void;
|
|
1547
|
+
}
|
|
1472
1548
|
export interface RunOptions {
|
|
1473
1549
|
/** Cancel the run by aborting this signal. */
|
|
1474
1550
|
readonly signal?: AbortSignal;
|
|
@@ -2107,12 +2183,16 @@ export interface ConnectedAccountCredential {
|
|
|
2107
2183
|
readonly lastRefreshAt: string | null;
|
|
2108
2184
|
readonly lastRefreshOutcome: "refreshed" | "fresh" | "failed" | null;
|
|
2109
2185
|
}
|
|
2186
|
+
export type { ConnectedAccountLogin, ConnectedAccountLoginClass };
|
|
2110
2187
|
export interface ConnectedAccount {
|
|
2111
2188
|
readonly connectedAccountId: string;
|
|
2112
2189
|
readonly ownerUserId: string;
|
|
2113
2190
|
readonly provider: ConnectedAccountProvider;
|
|
2114
2191
|
readonly name: string;
|
|
2115
|
-
/**
|
|
2192
|
+
/**
|
|
2193
|
+
* Provider-shaped payload kind: oauth-token | credentials-json | auth-json | gh-cli-token |
|
|
2194
|
+
* api-key.
|
|
2195
|
+
*/
|
|
2116
2196
|
readonly kind: string;
|
|
2117
2197
|
readonly status: ConnectedAccountStatus;
|
|
2118
2198
|
/** Non-secret display data (token suffix, codex account email, github login + scopes). */
|
|
@@ -2132,14 +2212,31 @@ export interface ConnectedAccount {
|
|
|
2132
2212
|
* from a control plane without `connectedAccountDefaults`.
|
|
2133
2213
|
*/
|
|
2134
2214
|
readonly credentialRevision?: number;
|
|
2215
|
+
/** Claude and Codex only: the account's login class. Absent from an older control plane. */
|
|
2216
|
+
readonly login?: ConnectedAccountLogin;
|
|
2217
|
+
}
|
|
2218
|
+
/** The owner's accounts, and whether they ever connected an API key. */
|
|
2219
|
+
export interface ConnectedAccountList {
|
|
2220
|
+
readonly accounts: readonly ConnectedAccount[];
|
|
2221
|
+
/**
|
|
2222
|
+
* Whether the owner ever connected an API key, archived ones included. Absent from a control
|
|
2223
|
+
* plane that does not report it.
|
|
2224
|
+
*/
|
|
2225
|
+
readonly hasApiKeyHistory?: boolean;
|
|
2135
2226
|
}
|
|
2136
2227
|
interface ConnectConnectedAccountBase {
|
|
2137
2228
|
readonly provider: ConnectedAccountProvider;
|
|
2138
2229
|
/**
|
|
2139
|
-
* Provider-shaped plaintext, passed through and sealed server-side: a Claude setup token
|
|
2140
|
-
* verbatim `.credentials.json`, verbatim Codex `auth.json
|
|
2230
|
+
* Provider-shaped plaintext, passed through and sealed server-side: a Claude setup token, API key
|
|
2231
|
+
* or verbatim `.credentials.json`, a verbatim Codex `auth.json` or an OpenAI API key, or a GitHub
|
|
2232
|
+
* token. Never logged.
|
|
2141
2233
|
*/
|
|
2142
2234
|
readonly secret: string;
|
|
2235
|
+
/**
|
|
2236
|
+
* Claude and Codex: the login class `secret` must be. A secret of the other class is refused
|
|
2237
|
+
* before anything is stored. Needs `features().connectedAccountLoginClass`.
|
|
2238
|
+
*/
|
|
2239
|
+
readonly login?: ConnectedAccountLoginClass;
|
|
2143
2240
|
/**
|
|
2144
2241
|
* Make the connected account the owner's default for the provider. A first account is the default
|
|
2145
2242
|
* without it. Needs `features().connectedAccountDefaults`.
|
|
@@ -2150,8 +2247,10 @@ interface ConnectConnectedAccountBase {
|
|
|
2150
2247
|
* Which account a connect writes. A person's first account for a provider is named `default`
|
|
2151
2248
|
* whatever `name` says. After that, `name` replaces the account of that name or makes a new one,
|
|
2152
2249
|
* and `name: "default"` replaces the chosen default. With neither `name` nor `accountId` (or
|
|
2153
|
-
* `default` while none is chosen),
|
|
2154
|
-
* with
|
|
2250
|
+
* `default` while none is chosen), the only account of the secret's login class is replaced, so a
|
|
2251
|
+
* key never replaces a subscription; with none of that class a new one is made (named `api-key` or
|
|
2252
|
+
* `subscription`), and with several the call fails with `reason` `connected-account-ambiguous` and
|
|
2253
|
+
* the candidates in `candidateAccountIds`.
|
|
2155
2254
|
*/
|
|
2156
2255
|
export type ConnectConnectedAccountOptions = ConnectConnectedAccountBase & ({
|
|
2157
2256
|
readonly name?: string;
|
|
@@ -2184,6 +2283,8 @@ export interface MarkConnectedAccountInvalidResult {
|
|
|
2184
2283
|
*/
|
|
2185
2284
|
export interface ConnectedAccountsNamespace {
|
|
2186
2285
|
list(): Promise<readonly ConnectedAccount[]>;
|
|
2286
|
+
/** The same accounts as `list`, with whether the owner ever connected an API key. */
|
|
2287
|
+
listWithHistory(): Promise<ConnectedAccountList>;
|
|
2187
2288
|
connect(options: ConnectConnectedAccountOptions): Promise<ConnectedAccount>;
|
|
2188
2289
|
/**
|
|
2189
2290
|
* Soft-archives the account; uniform not-found for "does not exist" and "not yours". Archiving
|
|
@@ -2241,4 +2342,3 @@ export interface SshKeysNamespace {
|
|
|
2241
2342
|
/** Archives the key; the gateway stops resolving it. */
|
|
2242
2343
|
remove(sshKeyId: string): Promise<SshKey>;
|
|
2243
2344
|
}
|
|
2244
|
-
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sealant/sdk-next",
|
|
3
|
-
"version": "0.40.0-next.
|
|
3
|
+
"version": "0.40.0-next.764",
|
|
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,12 +26,12 @@
|
|
|
26
26
|
"node": ">=20"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@sealant/api-contracts": "npm:@sealant/api-contracts-next@0.40.0-next.
|
|
29
|
+
"@sealant/api-contracts": "npm:@sealant/api-contracts-next@0.40.0-next.764"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
32
|
"effect": "^4.0.5"
|
|
33
33
|
},
|
|
34
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "4d306e5cd66c40f162f7346370edd82930c1bc02",
|
|
35
35
|
"publishConfig": {
|
|
36
36
|
"access": "public"
|
|
37
37
|
}
|