@sealant/sdk-next 0.40.0-next.763 → 0.40.0-next.765

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 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,7 +1029,9 @@ 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>;
1034
+ readonly credentialPlacements: import("effect/Schema").optional<import("effect/Schema").Boolean>;
989
1035
  }>>;
990
1036
  }>>, never, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
991
1037
  readonly getSetupState: HttpApiClient.Client.Method<import("effect/http-api/HttpApiEndpoint").HttpApiEndpoint<"getSetupState", "GET", "/v1/system/setup-state", never, never, never, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
@@ -1579,6 +1625,16 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
1579
1625
  readonly homes: import("effect/Schema").$Array<import("effect/Schema").Struct<{
1580
1626
  readonly home: import("effect/Schema").String;
1581
1627
  readonly onBehalfOfUserId: import("effect/Schema").String;
1628
+ readonly placements: import("effect/Schema").optional<import("effect/Schema").$Array<import("effect/Schema").Struct<{
1629
+ readonly placementId: import("effect/Schema").String;
1630
+ readonly accountId: import("effect/Schema").String;
1631
+ readonly revision: import("effect/Schema").Int;
1632
+ readonly login: import("effect/Schema").Struct<{
1633
+ readonly kind: import("effect/Schema").Literal<"subscription">;
1634
+ }>;
1635
+ readonly pinned: import("effect/Schema").Boolean;
1636
+ readonly provider: import("effect/Schema").Literals<readonly ["claude", "codex", "github", "pi", "opencode"]>;
1637
+ }>>>;
1582
1638
  readonly accounts: import("effect/Schema").Struct<{
1583
1639
  readonly claude: import("effect/Schema").optional<import("effect/Schema").Struct<{
1584
1640
  readonly connectedAccountId: import("effect/Schema").String;
@@ -1678,6 +1734,52 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
1678
1734
  readonly expiresAt: import("effect/Schema").optional<import("effect/Schema").String>;
1679
1735
  }>>;
1680
1736
  }>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").WorkspaceBadRequestError | typeof import("@sealant/api-contracts").WorkspaceInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
1737
+ readonly pruneWorkspaceCredentials: HttpApiClient.Client.Method<import("effect/http-api/HttpApiEndpoint").HttpApiEndpoint<"pruneWorkspaceCredentials", "POST", "/v1/workspaces/:workspaceId/credentials/prune", import("effect/Schema").toCodecStringTree<import("effect/Schema").Struct<{
1738
+ readonly workspaceId: import("effect/Schema").String;
1739
+ }>>, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
1740
+ readonly ownerUserId: import("effect/Schema").String;
1741
+ readonly home: import("effect/Schema").String;
1742
+ readonly live: import("effect/Schema").$Array<import("effect/Schema").String>;
1743
+ }>>, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
1744
+ readonly workspaceId: import("effect/Schema").String;
1745
+ readonly runId: import("effect/Schema").String;
1746
+ readonly home: import("effect/Schema").optional<import("effect/Schema").Struct<{
1747
+ readonly home: import("effect/Schema").String;
1748
+ readonly onBehalfOfUserId: import("effect/Schema").String;
1749
+ readonly placements: import("effect/Schema").optional<import("effect/Schema").$Array<import("effect/Schema").Struct<{
1750
+ readonly placementId: import("effect/Schema").String;
1751
+ readonly accountId: import("effect/Schema").String;
1752
+ readonly revision: import("effect/Schema").Int;
1753
+ readonly login: import("effect/Schema").Struct<{
1754
+ readonly kind: import("effect/Schema").Literal<"subscription">;
1755
+ }>;
1756
+ readonly pinned: import("effect/Schema").Boolean;
1757
+ readonly provider: import("effect/Schema").Literals<readonly ["claude", "codex", "github", "pi", "opencode"]>;
1758
+ }>>>;
1759
+ readonly accounts: import("effect/Schema").Struct<{
1760
+ readonly claude: import("effect/Schema").optional<import("effect/Schema").Struct<{
1761
+ readonly connectedAccountId: import("effect/Schema").String;
1762
+ readonly name: import("effect/Schema").String;
1763
+ }>>;
1764
+ readonly codex: import("effect/Schema").optional<import("effect/Schema").Struct<{
1765
+ readonly connectedAccountId: import("effect/Schema").String;
1766
+ readonly name: import("effect/Schema").String;
1767
+ }>>;
1768
+ readonly github: import("effect/Schema").optional<import("effect/Schema").Struct<{
1769
+ readonly connectedAccountId: import("effect/Schema").String;
1770
+ readonly name: import("effect/Schema").String;
1771
+ }>>;
1772
+ readonly pi: import("effect/Schema").optional<import("effect/Schema").Struct<{
1773
+ readonly connectedAccountId: import("effect/Schema").String;
1774
+ readonly name: import("effect/Schema").String;
1775
+ }>>;
1776
+ readonly opencode: import("effect/Schema").optional<import("effect/Schema").Struct<{
1777
+ readonly connectedAccountId: import("effect/Schema").String;
1778
+ readonly name: import("effect/Schema").String;
1779
+ }>>;
1780
+ }>;
1781
+ }>>;
1782
+ }>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").WorkspaceBadRequestError | typeof import("@sealant/api-contracts").WorkspaceForbiddenError | typeof import("@sealant/api-contracts").WorkspaceNotFoundError | typeof import("@sealant/api-contracts").WorkspaceConflictError | typeof import("@sealant/api-contracts").WorkspaceBadGatewayError | typeof import("@sealant/api-contracts").WorkspaceServiceUnavailableError | typeof import("@sealant/api-contracts").WorkspaceInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
1681
1783
  readonly putWorkspaceCredentials: HttpApiClient.Client.Method<import("effect/http-api/HttpApiEndpoint").HttpApiEndpoint<"putWorkspaceCredentials", "POST", "/v1/workspaces/:workspaceId/credentials", import("effect/Schema").toCodecStringTree<import("effect/Schema").Struct<{
1682
1784
  readonly workspaceId: import("effect/Schema").String;
1683
1785
  }>>, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
@@ -1692,12 +1794,23 @@ declare const buildControlPlaneClient: (config: SealantInternalConfig) => Effect
1692
1794
  readonly pi: import("effect/Schema").optional<import("effect/Schema").NullOr<import("effect/Schema").String>>;
1693
1795
  readonly opencode: import("effect/Schema").optional<import("effect/Schema").NullOr<import("effect/Schema").String>>;
1694
1796
  readonly partial: import("effect/Schema").optional<import("effect/Schema").Boolean>;
1797
+ readonly live: import("effect/Schema").optional<import("effect/Schema").$Array<import("effect/Schema").String>>;
1695
1798
  }>>, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
1696
1799
  readonly workspaceId: import("effect/Schema").String;
1697
1800
  readonly runId: import("effect/Schema").String;
1698
1801
  readonly home: import("effect/Schema").Struct<{
1699
1802
  readonly home: import("effect/Schema").String;
1700
1803
  readonly onBehalfOfUserId: import("effect/Schema").String;
1804
+ readonly placements: import("effect/Schema").optional<import("effect/Schema").$Array<import("effect/Schema").Struct<{
1805
+ readonly placementId: import("effect/Schema").String;
1806
+ readonly accountId: import("effect/Schema").String;
1807
+ readonly revision: import("effect/Schema").Int;
1808
+ readonly login: import("effect/Schema").Struct<{
1809
+ readonly kind: import("effect/Schema").Literal<"subscription">;
1810
+ }>;
1811
+ readonly pinned: import("effect/Schema").Boolean;
1812
+ readonly provider: import("effect/Schema").Literals<readonly ["claude", "codex", "github", "pi", "opencode"]>;
1813
+ }>>>;
1701
1814
  readonly accounts: import("effect/Schema").Struct<{
1702
1815
  readonly claude: import("effect/Schema").optional<import("effect/Schema").Struct<{
1703
1816
  readonly connectedAccountId: import("effect/Schema").String;
@@ -1895,6 +2008,12 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
1895
2008
  }>;
1896
2009
  readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
1897
2010
  readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
2011
+ readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
2012
+ readonly kind: import("effect/Schema").Literal<"subscription">;
2013
+ }>, import("effect/Schema").Struct<{
2014
+ readonly kind: import("effect/Schema").Literal<"api-key">;
2015
+ readonly suffix: import("effect/Schema").String;
2016
+ }>]>>;
1898
2017
  }>>, 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
2018
  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
2019
  readonly ownerUserId: import("effect/Schema").String;
@@ -1902,6 +2021,7 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
1902
2021
  readonly name: import("effect/Schema").optional<import("effect/Schema").String>;
1903
2022
  readonly accountId: import("effect/Schema").optional<import("effect/Schema").String>;
1904
2023
  readonly makeDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
2024
+ readonly login: import("effect/Schema").optional<import("effect/Schema").Literals<readonly ["subscription", "api-key"]>>;
1905
2025
  readonly secret: import("effect/Schema").String;
1906
2026
  }>>, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
1907
2027
  readonly connectedAccountId: import("effect/Schema").String;
@@ -1923,6 +2043,12 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
1923
2043
  }>;
1924
2044
  readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
1925
2045
  readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
2046
+ readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
2047
+ readonly kind: import("effect/Schema").Literal<"subscription">;
2048
+ }>, import("effect/Schema").Struct<{
2049
+ readonly kind: import("effect/Schema").Literal<"api-key">;
2050
+ readonly suffix: import("effect/Schema").String;
2051
+ }>]>>;
1926
2052
  }>>, 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
2053
  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
2054
  readonly ownerUserId: import("effect/Schema").String;
@@ -1947,7 +2073,14 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
1947
2073
  }>;
1948
2074
  readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
1949
2075
  readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
1950
- }>>;
2076
+ readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
2077
+ readonly kind: import("effect/Schema").Literal<"subscription">;
2078
+ }>, import("effect/Schema").Struct<{
2079
+ readonly kind: import("effect/Schema").Literal<"api-key">;
2080
+ readonly suffix: import("effect/Schema").String;
2081
+ }>]>>;
2082
+ }>>;
2083
+ readonly hasApiKeyHistory: import("effect/Schema").optional<import("effect/Schema").Boolean>;
1951
2084
  }>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").ConnectedAccountInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
1952
2085
  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
2086
  readonly connectedAccountId: import("effect/Schema").String;
@@ -1974,6 +2107,12 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
1974
2107
  }>;
1975
2108
  readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
1976
2109
  readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
2110
+ readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
2111
+ readonly kind: import("effect/Schema").Literal<"subscription">;
2112
+ }>, import("effect/Schema").Struct<{
2113
+ readonly kind: import("effect/Schema").Literal<"api-key">;
2114
+ readonly suffix: import("effect/Schema").String;
2115
+ }>]>>;
1977
2116
  readonly outcome: import("effect/Schema").optional<import("effect/Schema").Literals<readonly ["marked", "superseded"]>>;
1978
2117
  }>>, 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
2118
  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 +2140,12 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
2001
2140
  }>;
2002
2141
  readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
2003
2142
  readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
2143
+ readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
2144
+ readonly kind: import("effect/Schema").Literal<"subscription">;
2145
+ }>, import("effect/Schema").Struct<{
2146
+ readonly kind: import("effect/Schema").Literal<"api-key">;
2147
+ readonly suffix: import("effect/Schema").String;
2148
+ }>]>>;
2004
2149
  }>>, 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
2150
  };
2006
2151
  readonly github: {
@@ -2200,6 +2345,12 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
2200
2345
  }>;
2201
2346
  readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
2202
2347
  readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
2348
+ readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
2349
+ readonly kind: import("effect/Schema").Literal<"subscription">;
2350
+ }>, import("effect/Schema").Struct<{
2351
+ readonly kind: import("effect/Schema").Literal<"api-key">;
2352
+ readonly suffix: import("effect/Schema").String;
2353
+ }>]>>;
2203
2354
  }>;
2204
2355
  }>>;
2205
2356
  }>>, 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 +2400,12 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
2249
2400
  }>;
2250
2401
  readonly isDefault: import("effect/Schema").optional<import("effect/Schema").Boolean>;
2251
2402
  readonly credentialRevision: import("effect/Schema").optional<import("effect/Schema").Int>;
2403
+ readonly login: import("effect/Schema").optional<import("effect/Schema").Union<readonly [import("effect/Schema").Struct<{
2404
+ readonly kind: import("effect/Schema").Literal<"subscription">;
2405
+ }>, import("effect/Schema").Struct<{
2406
+ readonly kind: import("effect/Schema").Literal<"api-key">;
2407
+ readonly suffix: import("effect/Schema").String;
2408
+ }>]>>;
2252
2409
  }>;
2253
2410
  }>>;
2254
2411
  }>>, 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,7 +2961,9 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
2804
2961
  readonly personBinding: import("effect/Schema").optional<import("effect/Schema").Boolean>;
2805
2962
  readonly sshKeyRemovalEndsConnections: import("effect/Schema").optional<import("effect/Schema").Boolean>;
2806
2963
  readonly connectedAccountDefaults: import("effect/Schema").optional<import("effect/Schema").Boolean>;
2964
+ readonly connectedAccountLoginClass: import("effect/Schema").optional<import("effect/Schema").Boolean>;
2807
2965
  readonly kubernetesPersonLayout: import("effect/Schema").optional<import("effect/Schema").Boolean>;
2966
+ readonly credentialPlacements: import("effect/Schema").optional<import("effect/Schema").Boolean>;
2808
2967
  }>>;
2809
2968
  }>>, never, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
2810
2969
  readonly getSetupState: HttpApiClient.Client.Method<import("effect/http-api/HttpApiEndpoint").HttpApiEndpoint<"getSetupState", "GET", "/v1/system/setup-state", never, never, never, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
@@ -3398,6 +3557,16 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
3398
3557
  readonly homes: import("effect/Schema").$Array<import("effect/Schema").Struct<{
3399
3558
  readonly home: import("effect/Schema").String;
3400
3559
  readonly onBehalfOfUserId: import("effect/Schema").String;
3560
+ readonly placements: import("effect/Schema").optional<import("effect/Schema").$Array<import("effect/Schema").Struct<{
3561
+ readonly placementId: import("effect/Schema").String;
3562
+ readonly accountId: import("effect/Schema").String;
3563
+ readonly revision: import("effect/Schema").Int;
3564
+ readonly login: import("effect/Schema").Struct<{
3565
+ readonly kind: import("effect/Schema").Literal<"subscription">;
3566
+ }>;
3567
+ readonly pinned: import("effect/Schema").Boolean;
3568
+ readonly provider: import("effect/Schema").Literals<readonly ["claude", "codex", "github", "pi", "opencode"]>;
3569
+ }>>>;
3401
3570
  readonly accounts: import("effect/Schema").Struct<{
3402
3571
  readonly claude: import("effect/Schema").optional<import("effect/Schema").Struct<{
3403
3572
  readonly connectedAccountId: import("effect/Schema").String;
@@ -3497,6 +3666,52 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
3497
3666
  readonly expiresAt: import("effect/Schema").optional<import("effect/Schema").String>;
3498
3667
  }>>;
3499
3668
  }>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").WorkspaceBadRequestError | typeof import("@sealant/api-contracts").WorkspaceInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
3669
+ readonly pruneWorkspaceCredentials: HttpApiClient.Client.Method<import("effect/http-api/HttpApiEndpoint").HttpApiEndpoint<"pruneWorkspaceCredentials", "POST", "/v1/workspaces/:workspaceId/credentials/prune", import("effect/Schema").toCodecStringTree<import("effect/Schema").Struct<{
3670
+ readonly workspaceId: import("effect/Schema").String;
3671
+ }>>, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
3672
+ readonly ownerUserId: import("effect/Schema").String;
3673
+ readonly home: import("effect/Schema").String;
3674
+ readonly live: import("effect/Schema").$Array<import("effect/Schema").String>;
3675
+ }>>, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
3676
+ readonly workspaceId: import("effect/Schema").String;
3677
+ readonly runId: import("effect/Schema").String;
3678
+ readonly home: import("effect/Schema").optional<import("effect/Schema").Struct<{
3679
+ readonly home: import("effect/Schema").String;
3680
+ readonly onBehalfOfUserId: import("effect/Schema").String;
3681
+ readonly placements: import("effect/Schema").optional<import("effect/Schema").$Array<import("effect/Schema").Struct<{
3682
+ readonly placementId: import("effect/Schema").String;
3683
+ readonly accountId: import("effect/Schema").String;
3684
+ readonly revision: import("effect/Schema").Int;
3685
+ readonly login: import("effect/Schema").Struct<{
3686
+ readonly kind: import("effect/Schema").Literal<"subscription">;
3687
+ }>;
3688
+ readonly pinned: import("effect/Schema").Boolean;
3689
+ readonly provider: import("effect/Schema").Literals<readonly ["claude", "codex", "github", "pi", "opencode"]>;
3690
+ }>>>;
3691
+ readonly accounts: import("effect/Schema").Struct<{
3692
+ readonly claude: import("effect/Schema").optional<import("effect/Schema").Struct<{
3693
+ readonly connectedAccountId: import("effect/Schema").String;
3694
+ readonly name: import("effect/Schema").String;
3695
+ }>>;
3696
+ readonly codex: import("effect/Schema").optional<import("effect/Schema").Struct<{
3697
+ readonly connectedAccountId: import("effect/Schema").String;
3698
+ readonly name: import("effect/Schema").String;
3699
+ }>>;
3700
+ readonly github: import("effect/Schema").optional<import("effect/Schema").Struct<{
3701
+ readonly connectedAccountId: import("effect/Schema").String;
3702
+ readonly name: import("effect/Schema").String;
3703
+ }>>;
3704
+ readonly pi: import("effect/Schema").optional<import("effect/Schema").Struct<{
3705
+ readonly connectedAccountId: import("effect/Schema").String;
3706
+ readonly name: import("effect/Schema").String;
3707
+ }>>;
3708
+ readonly opencode: import("effect/Schema").optional<import("effect/Schema").Struct<{
3709
+ readonly connectedAccountId: import("effect/Schema").String;
3710
+ readonly name: import("effect/Schema").String;
3711
+ }>>;
3712
+ }>;
3713
+ }>>;
3714
+ }>>, import("effect/Schema").toCodecJson<typeof import("@sealant/api-contracts").WorkspaceBadRequestError | typeof import("@sealant/api-contracts").WorkspaceForbiddenError | typeof import("@sealant/api-contracts").WorkspaceNotFoundError | typeof import("@sealant/api-contracts").WorkspaceConflictError | typeof import("@sealant/api-contracts").WorkspaceBadGatewayError | typeof import("@sealant/api-contracts").WorkspaceServiceUnavailableError | typeof import("@sealant/api-contracts").WorkspaceInternalServerError>, import("@sealant/api-contracts").RequestRefusal, never>, never, never>;
3500
3715
  readonly putWorkspaceCredentials: HttpApiClient.Client.Method<import("effect/http-api/HttpApiEndpoint").HttpApiEndpoint<"putWorkspaceCredentials", "POST", "/v1/workspaces/:workspaceId/credentials", import("effect/Schema").toCodecStringTree<import("effect/Schema").Struct<{
3501
3716
  readonly workspaceId: import("effect/Schema").String;
3502
3717
  }>>, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
@@ -3511,12 +3726,23 @@ declare const SealantApiClient_base: Context.ServiceClass<SealantApiClient, "@se
3511
3726
  readonly pi: import("effect/Schema").optional<import("effect/Schema").NullOr<import("effect/Schema").String>>;
3512
3727
  readonly opencode: import("effect/Schema").optional<import("effect/Schema").NullOr<import("effect/Schema").String>>;
3513
3728
  readonly partial: import("effect/Schema").optional<import("effect/Schema").Boolean>;
3729
+ readonly live: import("effect/Schema").optional<import("effect/Schema").$Array<import("effect/Schema").String>>;
3514
3730
  }>>, never, import("effect/Schema").toCodecJson<import("effect/Schema").Struct<{
3515
3731
  readonly workspaceId: import("effect/Schema").String;
3516
3732
  readonly runId: import("effect/Schema").String;
3517
3733
  readonly home: import("effect/Schema").Struct<{
3518
3734
  readonly home: import("effect/Schema").String;
3519
3735
  readonly onBehalfOfUserId: import("effect/Schema").String;
3736
+ readonly placements: import("effect/Schema").optional<import("effect/Schema").$Array<import("effect/Schema").Struct<{
3737
+ readonly placementId: import("effect/Schema").String;
3738
+ readonly accountId: import("effect/Schema").String;
3739
+ readonly revision: import("effect/Schema").Int;
3740
+ readonly login: import("effect/Schema").Struct<{
3741
+ readonly kind: import("effect/Schema").Literal<"subscription">;
3742
+ }>;
3743
+ readonly pinned: import("effect/Schema").Boolean;
3744
+ readonly provider: import("effect/Schema").Literals<readonly ["claude", "codex", "github", "pi", "opencode"]>;
3745
+ }>>>;
3520
3746
  readonly accounts: import("effect/Schema").Struct<{
3521
3747
  readonly claude: import("effect/Schema").optional<import("effect/Schema").Struct<{
3522
3748
  readonly connectedAccountId: import("effect/Schema").String;
@@ -562,12 +562,23 @@ export declare const putWorkspaceCredentialsOp: (workspaceId: string, payload: {
562
562
  readonly pi?: string | null | undefined;
563
563
  readonly opencode?: string | null | undefined;
564
564
  readonly partial?: boolean | undefined;
565
+ readonly live?: readonly string[] | undefined;
565
566
  }) => Effect.Effect<{
566
567
  readonly workspaceId: string;
567
568
  readonly runId: string;
568
569
  readonly home: {
569
570
  readonly home: string;
570
571
  readonly onBehalfOfUserId: string;
572
+ readonly placements?: readonly {
573
+ readonly placementId: string;
574
+ readonly accountId: string;
575
+ readonly revision: number;
576
+ readonly login: {
577
+ readonly kind: "subscription";
578
+ };
579
+ readonly pinned: boolean;
580
+ readonly provider: "claude" | "codex" | "github" | "opencode" | "pi";
581
+ }[] | undefined;
571
582
  readonly accounts: {
572
583
  readonly claude?: {
573
584
  readonly connectedAccountId: string;
@@ -597,6 +608,51 @@ export declare const putWorkspaceCredentialsOp: (workspaceId: string, payload: {
597
608
  readonly message: string;
598
609
  }[] | undefined;
599
610
  }, import("effect/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RequestRefusedError | 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>;
611
+ /** Remove unused credential placements in one home. */
612
+ export declare const pruneWorkspaceCredentialsOp: (workspaceId: string, payload: {
613
+ readonly ownerUserId: string;
614
+ readonly home: string;
615
+ readonly live: readonly string[];
616
+ }) => Effect.Effect<{
617
+ readonly workspaceId: string;
618
+ readonly runId: string;
619
+ readonly home?: {
620
+ readonly home: string;
621
+ readonly onBehalfOfUserId: string;
622
+ readonly placements?: readonly {
623
+ readonly placementId: string;
624
+ readonly accountId: string;
625
+ readonly revision: number;
626
+ readonly login: {
627
+ readonly kind: "subscription";
628
+ };
629
+ readonly pinned: boolean;
630
+ readonly provider: "claude" | "codex" | "github" | "opencode" | "pi";
631
+ }[] | undefined;
632
+ readonly accounts: {
633
+ readonly claude?: {
634
+ readonly connectedAccountId: string;
635
+ readonly name: string;
636
+ } | undefined;
637
+ readonly codex?: {
638
+ readonly connectedAccountId: string;
639
+ readonly name: string;
640
+ } | undefined;
641
+ readonly github?: {
642
+ readonly connectedAccountId: string;
643
+ readonly name: string;
644
+ } | undefined;
645
+ readonly pi?: {
646
+ readonly connectedAccountId: string;
647
+ readonly name: string;
648
+ } | undefined;
649
+ readonly opencode?: {
650
+ readonly connectedAccountId: string;
651
+ readonly name: string;
652
+ } | undefined;
653
+ };
654
+ } | undefined;
655
+ }, import("effect/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RequestRefusedError | 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>;
600
656
  export declare const releaseWorkspaceCredentialsOp: (workspaceId: string, query: {
601
657
  readonly ownerUserId: string;
602
658
  readonly home: string;
@@ -614,6 +670,16 @@ export declare const listWorkspaceCredentialsOp: (workspaceId: string, query: {
614
670
  readonly homes: readonly {
615
671
  readonly home: string;
616
672
  readonly onBehalfOfUserId: string;
673
+ readonly placements?: readonly {
674
+ readonly placementId: string;
675
+ readonly accountId: string;
676
+ readonly revision: number;
677
+ readonly login: {
678
+ readonly kind: "subscription";
679
+ };
680
+ readonly pinned: boolean;
681
+ readonly provider: "claude" | "codex" | "github" | "opencode" | "pi";
682
+ }[] | undefined;
617
683
  readonly accounts: {
618
684
  readonly claude?: {
619
685
  readonly connectedAccountId: string;
@@ -1111,7 +1177,14 @@ export declare const listConnectedAccountsOp: (ownerUserId: string) => Effect.Ef
1111
1177
  };
1112
1178
  readonly isDefault?: boolean | undefined;
1113
1179
  readonly credentialRevision?: number | undefined;
1180
+ readonly login?: {
1181
+ readonly kind: "subscription";
1182
+ } | {
1183
+ readonly kind: "api-key";
1184
+ readonly suffix: string;
1185
+ } | undefined;
1114
1186
  }[];
1187
+ readonly hasApiKeyHistory?: boolean | undefined;
1115
1188
  }, import("@sealant/api-contracts").ConnectedAccountInternalServerError | import("effect/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RequestRefusedError | import("effect/Schema").SchemaError, SealantApiClient>;
1116
1189
  export declare const createConnectedAccountOp: (payload: {
1117
1190
  readonly ownerUserId: string;
@@ -1119,6 +1192,7 @@ export declare const createConnectedAccountOp: (payload: {
1119
1192
  readonly name?: string | undefined;
1120
1193
  readonly accountId?: string | undefined;
1121
1194
  readonly makeDefault?: boolean | undefined;
1195
+ readonly login?: "api-key" | "subscription" | undefined;
1122
1196
  readonly secret: string;
1123
1197
  }) => Effect.Effect<{
1124
1198
  readonly connectedAccountId: string;
@@ -1142,6 +1216,12 @@ export declare const createConnectedAccountOp: (payload: {
1142
1216
  };
1143
1217
  readonly isDefault?: boolean | undefined;
1144
1218
  readonly credentialRevision?: number | undefined;
1219
+ readonly login?: {
1220
+ readonly kind: "subscription";
1221
+ } | {
1222
+ readonly kind: "api-key";
1223
+ readonly suffix: string;
1224
+ } | undefined;
1145
1225
  }, 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
1226
  export declare const archiveConnectedAccountOp: (connectedAccountId: string, ownerUserId: string) => Effect.Effect<{
1147
1227
  readonly connectedAccountId: string;
@@ -1165,6 +1245,12 @@ export declare const archiveConnectedAccountOp: (connectedAccountId: string, own
1165
1245
  };
1166
1246
  readonly isDefault?: boolean | undefined;
1167
1247
  readonly credentialRevision?: number | undefined;
1248
+ readonly login?: {
1249
+ readonly kind: "subscription";
1250
+ } | {
1251
+ readonly kind: "api-key";
1252
+ readonly suffix: string;
1253
+ } | undefined;
1168
1254
  }, 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
1255
  export declare const setConnectedAccountDefaultOp: (provider: "claude" | "codex" | "github", payload: {
1170
1256
  readonly ownerUserId: string;
@@ -1191,6 +1277,12 @@ export declare const setConnectedAccountDefaultOp: (provider: "claude" | "codex"
1191
1277
  };
1192
1278
  readonly isDefault?: boolean | undefined;
1193
1279
  readonly credentialRevision?: number | undefined;
1280
+ readonly login?: {
1281
+ readonly kind: "subscription";
1282
+ } | {
1283
+ readonly kind: "api-key";
1284
+ readonly suffix: string;
1285
+ } | undefined;
1194
1286
  }, 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
1287
  export declare const markConnectedAccountInvalidOp: (connectedAccountId: string, payload: {
1196
1288
  readonly ownerUserId: string;
@@ -1217,6 +1309,12 @@ export declare const markConnectedAccountInvalidOp: (connectedAccountId: string,
1217
1309
  };
1218
1310
  readonly isDefault?: boolean | undefined;
1219
1311
  readonly credentialRevision?: number | undefined;
1312
+ readonly login?: {
1313
+ readonly kind: "subscription";
1314
+ } | {
1315
+ readonly kind: "api-key";
1316
+ readonly suffix: string;
1317
+ } | undefined;
1220
1318
  readonly outcome?: "marked" | "superseded" | undefined;
1221
1319
  }, 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
1320
  export declare const inferenceRespondOp: (payload: {
@@ -1280,7 +1378,9 @@ export declare const getIndexOp: () => Effect.Effect<{
1280
1378
  readonly personBinding?: boolean | undefined;
1281
1379
  readonly sshKeyRemovalEndsConnections?: boolean | undefined;
1282
1380
  readonly connectedAccountDefaults?: boolean | undefined;
1381
+ readonly connectedAccountLoginClass?: boolean | undefined;
1283
1382
  readonly kubernetesPersonLayout?: boolean | undefined;
1383
+ readonly credentialPlacements?: boolean | undefined;
1284
1384
  } | undefined;
1285
1385
  }, import("effect/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RequestRefusedError | import("effect/Schema").SchemaError, SealantApiClient>;
1286
1386
  export declare const getSetupStateOp: () => Effect.Effect<{
@@ -28,6 +28,8 @@ export const recoverWorkspaceOp = (workspaceId, payload) => Effect.flatMap(Seala
28
28
  export const restartWorkspaceOp = (workspaceId, payload) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.restartWorkspace({ params: { workspaceId }, payload }));
29
29
  export const inspectWorkspaceImageOp = (payload) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.inspectWorkspaceImage({ payload }));
30
30
  export const putWorkspaceCredentialsOp = (workspaceId, payload) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.putWorkspaceCredentials({ params: { workspaceId }, payload }));
31
+ /** Remove unused credential placements in one home. */
32
+ export const pruneWorkspaceCredentialsOp = (workspaceId, payload) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.pruneWorkspaceCredentials({ params: { workspaceId }, payload }));
31
33
  export const releaseWorkspaceCredentialsOp = (workspaceId, query) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.releaseWorkspaceCredentials({ params: { workspaceId }, query }));
32
34
  export const listWorkspaceCredentialsOp = (workspaceId, query) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.listWorkspaceCredentials({ params: { workspaceId }, query }));
33
35
  export const expireWorkspaceOp = (workspaceId, payload) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.expireWorkspace({ params: { workspaceId }, payload }));
@@ -1,11 +1,12 @@
1
1
  import { sessionArgvIssue } from "@sealant/api-contracts";
2
2
  import { applyDotfiles } from "../effect/apply-dotfiles.js";
3
3
  import { execWorkspace } from "../effect/exec-workspace.js";
4
- import { bindWorkspaceOp, flushWorkspaceCaptureOp, getWorkspaceCaptureStatusOp, createSessionAsUserOp, createSessionOp, expireWorkspaceOp, clearWorkspaceSshUserOp, getSessionOp, getWorkspaceOp, listSessionsOp, replanWorkspaceCaptureOp, recoverWorkspaceOp, restartWorkspaceOp, listWorkspaceCredentialsOp, putWorkspaceCredentialsOp, releaseWorkspaceCredentialsOp, stopWorkspaceOp, } from "../effect/operations.js";
4
+ import { bindWorkspaceOp, flushWorkspaceCaptureOp, getWorkspaceCaptureStatusOp, createSessionAsUserOp, createSessionOp, expireWorkspaceOp, clearWorkspaceSshUserOp, getSessionOp, getWorkspaceOp, listSessionsOp, replanWorkspaceCaptureOp, recoverWorkspaceOp, restartWorkspaceOp, listWorkspaceCredentialsOp, putWorkspaceCredentialsOp, pruneWorkspaceCredentialsOp, releaseWorkspaceCredentialsOp, stopWorkspaceOp, } from "../effect/operations.js";
5
5
  import { SealantApiError, SealantError, SealantNotImplementedError } from "../errors.js";
6
6
  import { elapsed } from "../internal/clock.js";
7
7
  import { mapAccountRef } from "../internal/credentials.js";
8
8
  import { parseTtlSeconds } from "../internal/duration.js";
9
+ import { requireCredentialPlacements } from "../internal/features.js";
9
10
  import { requireProcessUser } from "../internal/process-user.js";
10
11
  import { openForward, openForwardV2 } from "./forward.js";
11
12
  import { makeInteractiveSession } from "./session.js";
@@ -201,6 +202,7 @@ export const toWorkspaceImage = (image) => ({
201
202
  });
202
203
  /** Wire → public credential home. */
203
204
  const toCredentialHome = (home) => ({
205
+ ...(home.placements === undefined ? {} : { placements: home.placements }),
204
206
  home: home.home,
205
207
  onBehalfOf: home.onBehalfOfUserId,
206
208
  accounts: {
@@ -759,6 +761,8 @@ export const makeWorkspace = (ctx, init) => {
759
761
  // People's logins, one person per home; the workspace stays the client owner's.
760
762
  credentials: {
761
763
  put: async (options) => {
764
+ if (options.live !== undefined)
765
+ await requireCredentialPlacements(ctx);
762
766
  const choices = {
763
767
  claude: accountChoice(options.claude),
764
768
  codex: accountChoice(options.codex),
@@ -778,6 +782,7 @@ export const makeWorkspace = (ctx, init) => {
778
782
  ...(choices.pi === undefined ? {} : { pi: choices.pi }),
779
783
  ...(choices.opencode === undefined ? {} : { opencode: choices.opencode }),
780
784
  ...(options.partial === true ? { partial: true } : {}),
785
+ ...(options.live === undefined ? {} : { live: options.live }),
781
786
  }));
782
787
  return {
783
788
  ...toCredentialHome(answered.home),
@@ -788,6 +793,15 @@ export const makeWorkspace = (ctx, init) => {
788
793
  })),
789
794
  };
790
795
  },
796
+ prune: async (home, live) => {
797
+ await requireCredentialPlacements(ctx);
798
+ const answered = await ctx.runtime.run(pruneWorkspaceCredentialsOp(init.id, {
799
+ ownerUserId: ctx.config.hostLocal.ownerUserId,
800
+ home,
801
+ live,
802
+ }));
803
+ return answered.home === undefined ? undefined : toCredentialHome(answered.home);
804
+ },
791
805
  release: async (home) => {
792
806
  const answered = await ctx.runtime.run(releaseWorkspaceCredentialsOp(init.id, {
793
807
  ownerUserId: ctx.config.hostLocal.ownerUserId,
@@ -17,7 +17,9 @@ 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;
22
+ readonly credentialPlacements?: boolean | undefined;
21
23
  } | undefined;
22
24
  }) => SealantFeatures;
23
25
  /**
@@ -26,3 +28,5 @@ export declare const toFeatures: (index: {
26
28
  */
27
29
  export declare const readFeatures: (ctx: SdkContext) => Promise<SealantFeatures>;
28
30
  export declare const requireConnectedAccountDefaults: (ctx: SdkContext) => Promise<void>;
31
+ export declare const requireConnectedAccountLoginClass: (ctx: SdkContext) => Promise<void>;
32
+ export declare const requireCredentialPlacements: (ctx: SdkContext) => Promise<void>;
@@ -20,7 +20,9 @@ export const toFeatures = (index) => {
20
20
  workspaceSshUser: features?.workspaceSshUser === true,
21
21
  personBinding: features?.personBinding === true,
22
22
  sshKeyRemovalEndsConnections: features?.sshKeyRemovalEndsConnections === true,
23
+ credentialPlacements: features?.credentialPlacements === true,
23
24
  connectedAccountDefaults: features?.connectedAccountDefaults === true,
25
+ connectedAccountLoginClass: features?.connectedAccountLoginClass === true,
24
26
  kubernetesPersonLayout: features?.kubernetesPersonLayout === true,
25
27
  };
26
28
  };
@@ -50,3 +52,14 @@ export const requireConnectedAccountDefaults = async (ctx) => {
50
52
  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
53
  }
52
54
  };
55
+ export const requireConnectedAccountLoginClass = async (ctx) => {
56
+ const supported = await readFeatures(ctx).then((features) => features.connectedAccountLoginClass, () => false);
57
+ if (!supported) {
58
+ 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" });
59
+ }
60
+ };
61
+ export const requireCredentialPlacements = async (ctx) => {
62
+ const supported = await readFeatures(ctx).then((features) => features.credentialPlacements, () => false);
63
+ if (!supported)
64
+ throw new SealantError("This control plane cannot protect live credential placements or prune them; nothing was sent.", { code: "credential-placements-unsupported" });
65
+ };
package/dist/types.d.ts CHANGED
@@ -15,7 +15,7 @@
15
15
  * `SealantNotImplementedError` at runtime, see `./errors.js`).
16
16
  */
17
17
  import type { ReadableStream as NodeReadableStream, WritableStream as NodeWritableStream } from "node:stream/web";
18
- import type { ForwardBinding, ForwardResetReason } from "@sealant/api-contracts";
18
+ import type { ConnectedAccountLogin, ConnectedAccountLoginClass, ForwardBinding, ForwardResetReason } from "@sealant/api-contracts";
19
19
  /**
20
20
  * Public client configuration. Intentionally minimal: a base URL and an API key. Host-local
21
21
  * concerns required by the current slice (owner identity, registry, direct database access) live in
@@ -1186,7 +1186,7 @@ export interface WorkspaceCredentials {
1186
1186
  * (missing, not a directory, reached through a symbolic link, or a login directory linking out
1187
1187
  * of it), `home-busy` (another write into it is still running: retry), `workspace-not-running`,
1188
1188
  * `connected-account-invalid`, `connected-account-unsupported` (a Codex account named for pi or
1189
- * 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
1190
1190
  * `WorkspaceNotFoundError` for an account `onBehalfOf` cannot name; with
1191
1191
  * `WorkspaceBadGatewayError` when the executor did not confirm the write. The body code is
1192
1192
  * `SealantApiError.reason`; a refused account is `reason` `connected-account-missing` (404) or
@@ -1203,6 +1203,11 @@ export interface WorkspaceCredentials {
1203
1203
  }>;
1204
1204
  /** The homes of the running executor and the logins Core keeps in each, oldest first. */
1205
1205
  list(): Promise<readonly WorkspaceCredentialHome[]>;
1206
+ /**
1207
+ * Remove every login of `home` that is neither in `live` (the placements running processes hold)
1208
+ * nor the person's default. Answers the home as it is left, or `undefined` when it holds nothing.
1209
+ */
1210
+ prune(home: string, live: readonly string[]): Promise<WorkspaceCredentialHome | undefined>;
1206
1211
  }
1207
1212
  /**
1208
1213
  * A person's dotfiles in a running workspace (see {@link Workspace.dotfiles}): the same sources as a
@@ -1313,6 +1318,11 @@ export interface WorkspaceCredentialsPutOptions {
1313
1318
  * which and why, instead of the whole put rejecting. Any other refusal still rejects.
1314
1319
  */
1315
1320
  readonly partial?: boolean;
1321
+ /**
1322
+ * The placements running processes hold in this home: a provider whose file one of them holds is
1323
+ * not written over with another login. Requires `features().credentialPlacements`.
1324
+ */
1325
+ readonly live?: readonly string[];
1316
1326
  }
1317
1327
  /** A provider a partial {@link WorkspaceCredentials.put} left out, and why. */
1318
1328
  export interface WorkspaceCredentialSkip {
@@ -1328,6 +1338,20 @@ export interface WorkspaceCredentialsPutResult extends WorkspaceCredentialHome {
1328
1338
  /** A partial put's left-out providers, in the order named; always empty for a whole put. */
1329
1339
  readonly skipped: readonly WorkspaceCredentialSkip[];
1330
1340
  }
1341
+ /** One login written into one home: its id stays the same while a refresh moves its revision. */
1342
+ export interface CredentialPlacementReceipt {
1343
+ readonly placementId: string;
1344
+ readonly accountId: string;
1345
+ readonly revision: number;
1346
+ readonly login: {
1347
+ readonly kind: "subscription";
1348
+ };
1349
+ readonly pinned: boolean;
1350
+ }
1351
+ /** A receipt associated with the provider slot it occupies in this home. */
1352
+ export interface WorkspaceCredentialPlacementReceipt extends CredentialPlacementReceipt {
1353
+ readonly provider: "claude" | "codex" | "github" | "pi" | "opencode";
1354
+ }
1331
1355
  /** One account whose copy a home holds. */
1332
1356
  export interface WorkspaceCredentialHomeAccount {
1333
1357
  readonly connectedAccountId: string;
@@ -1336,6 +1360,8 @@ export interface WorkspaceCredentialHomeAccount {
1336
1360
  }
1337
1361
  /** One home of a running workspace and the logins Core keeps in it. */
1338
1362
  export interface WorkspaceCredentialHome {
1363
+ /** One receipt per login written there. Absent from a control plane without placements. */
1364
+ readonly placements?: readonly WorkspaceCredentialPlacementReceipt[];
1339
1365
  readonly home: string;
1340
1366
  /** The one person whose logins the home holds. */
1341
1367
  readonly onBehalfOf: string;
@@ -1413,12 +1439,20 @@ export interface SealantFeatures {
1413
1439
  * `default`.
1414
1440
  */
1415
1441
  readonly connectedAccountDefaults: boolean;
1442
+ /**
1443
+ * `login` on `connect()` (a secret of the other login class is refused), Claude and Codex API
1444
+ * keys beside a subscription, `ConnectedAccount.login`, and `hasApiKeyHistory` from
1445
+ * `connectedAccounts.listWithHistory()`.
1446
+ */
1447
+ readonly connectedAccountLoginClass: boolean;
1416
1448
  /**
1417
1449
  * `ownerMap` is taken on Kubernetes (runc): each person's `sudo` works in the Pod, and images the
1418
1450
  * cluster builds record the probe a per-person launch checks. From an older control plane a
1419
1451
  * create with `ownerMap` on Kubernetes is refused.
1420
1452
  */
1421
1453
  readonly kubernetesPersonLayout: boolean;
1454
+ /** Credential homes answer placement receipts; a put takes `live`; `prune` exists. */
1455
+ readonly credentialPlacements: boolean;
1422
1456
  }
1423
1457
  /** A published workspace image. */
1424
1458
  export interface WorkspaceImage {
@@ -2177,12 +2211,16 @@ export interface ConnectedAccountCredential {
2177
2211
  readonly lastRefreshAt: string | null;
2178
2212
  readonly lastRefreshOutcome: "refreshed" | "fresh" | "failed" | null;
2179
2213
  }
2214
+ export type { ConnectedAccountLogin, ConnectedAccountLoginClass };
2180
2215
  export interface ConnectedAccount {
2181
2216
  readonly connectedAccountId: string;
2182
2217
  readonly ownerUserId: string;
2183
2218
  readonly provider: ConnectedAccountProvider;
2184
2219
  readonly name: string;
2185
- /** Provider-shaped payload kind: oauth-token | credentials-json | auth-json | gh-cli-token. */
2220
+ /**
2221
+ * Provider-shaped payload kind: oauth-token | credentials-json | auth-json | gh-cli-token |
2222
+ * api-key.
2223
+ */
2186
2224
  readonly kind: string;
2187
2225
  readonly status: ConnectedAccountStatus;
2188
2226
  /** Non-secret display data (token suffix, codex account email, github login + scopes). */
@@ -2202,14 +2240,31 @@ export interface ConnectedAccount {
2202
2240
  * from a control plane without `connectedAccountDefaults`.
2203
2241
  */
2204
2242
  readonly credentialRevision?: number;
2243
+ /** Claude and Codex only: the account's login class. Absent from an older control plane. */
2244
+ readonly login?: ConnectedAccountLogin;
2245
+ }
2246
+ /** The owner's accounts, and whether they ever connected an API key. */
2247
+ export interface ConnectedAccountList {
2248
+ readonly accounts: readonly ConnectedAccount[];
2249
+ /**
2250
+ * Whether the owner ever connected an API key, archived ones included. Absent from a control
2251
+ * plane that does not report it.
2252
+ */
2253
+ readonly hasApiKeyHistory?: boolean;
2205
2254
  }
2206
2255
  interface ConnectConnectedAccountBase {
2207
2256
  readonly provider: ConnectedAccountProvider;
2208
2257
  /**
2209
- * Provider-shaped plaintext, passed through and sealed server-side: a Claude setup token or
2210
- * verbatim `.credentials.json`, verbatim Codex `auth.json`, or a GitHub token. Never logged.
2258
+ * Provider-shaped plaintext, passed through and sealed server-side: a Claude setup token, API key
2259
+ * or verbatim `.credentials.json`, a verbatim Codex `auth.json` or an OpenAI API key, or a GitHub
2260
+ * token. Never logged.
2211
2261
  */
2212
2262
  readonly secret: string;
2263
+ /**
2264
+ * Claude and Codex: the login class `secret` must be. A secret of the other class is refused
2265
+ * before anything is stored. Needs `features().connectedAccountLoginClass`.
2266
+ */
2267
+ readonly login?: ConnectedAccountLoginClass;
2213
2268
  /**
2214
2269
  * Make the connected account the owner's default for the provider. A first account is the default
2215
2270
  * without it. Needs `features().connectedAccountDefaults`.
@@ -2220,8 +2275,10 @@ interface ConnectConnectedAccountBase {
2220
2275
  * Which account a connect writes. A person's first account for a provider is named `default`
2221
2276
  * whatever `name` says. After that, `name` replaces the account of that name or makes a new one,
2222
2277
  * and `name: "default"` replaces the chosen default. With neither `name` nor `accountId` (or
2223
- * `default` while none is chosen), an only account is replaced, and with several the call fails
2224
- * with `reason` `connected-account-ambiguous` and the candidates in `candidateAccountIds`.
2278
+ * `default` while none is chosen), the only account of the secret's login class is replaced, so a
2279
+ * key never replaces a subscription; with none of that class a new one is made (named `api-key` or
2280
+ * `subscription`), and with several the call fails with `reason` `connected-account-ambiguous` and
2281
+ * the candidates in `candidateAccountIds`.
2225
2282
  */
2226
2283
  export type ConnectConnectedAccountOptions = ConnectConnectedAccountBase & ({
2227
2284
  readonly name?: string;
@@ -2254,6 +2311,8 @@ export interface MarkConnectedAccountInvalidResult {
2254
2311
  */
2255
2312
  export interface ConnectedAccountsNamespace {
2256
2313
  list(): Promise<readonly ConnectedAccount[]>;
2314
+ /** The same accounts as `list`, with whether the owner ever connected an API key. */
2315
+ listWithHistory(): Promise<ConnectedAccountList>;
2257
2316
  connect(options: ConnectConnectedAccountOptions): Promise<ConnectedAccount>;
2258
2317
  /**
2259
2318
  * Soft-archives the account; uniform not-found for "does not exist" and "not yours". Archiving
@@ -2311,4 +2370,3 @@ export interface SshKeysNamespace {
2311
2370
  /** Archives the key; the gateway stops resolving it. */
2312
2371
  remove(sshKeyId: string): Promise<SshKey>;
2313
2372
  }
2314
- export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sealant/sdk-next",
3
- "version": "0.40.0-next.763",
3
+ "version": "0.40.0-next.765",
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.763"
29
+ "@sealant/api-contracts": "npm:@sealant/api-contracts-next@0.40.0-next.765"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "effect": "^4.0.5"
33
33
  },
34
- "gitHead": "c741e10743b2c553b526f93565c256fe8fa4bd8b",
34
+ "gitHead": "9802ef32e54645b875272fecf15d036bf7f095e1",
35
35
  "publishConfig": {
36
36
  "access": "public"
37
37
  }