@gitterm/sdk 0.4.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,12 +23,12 @@ Create a workspace, run a prompt, and get the final result:
23
23
  import { createGittermClient } from "@gitterm/sdk";
24
24
 
25
25
  const client = createGittermClient({ token: process.env.GITTERM_API_TOKEN });
26
- const [github] = await client.integrations.github.list();
26
+ const [github] = await client.integrations.connections.list({ integration: "github" });
27
27
  if (!github) throw new Error("Connect GitHub in the Gitterm dashboard first");
28
28
 
29
29
  const { workspace } = await client.workspaces.create({
30
30
  repo: "https://github.com/acme/product",
31
- gitIntegrationId: github.id,
31
+ connections: [github.id],
32
32
  autoTerminateAfterMs: 2 * 60 * 60 * 1000,
33
33
  });
34
34
 
@@ -45,16 +45,17 @@ try {
45
45
  }
46
46
  ```
47
47
 
48
- Connected cloud identities are discoverable without returning credentials:
48
+ Every integration you can attach, personal or admin-provided, is a **connection** with one shape.
49
+ Attach any number by id (at most one per integration):
49
50
 
50
51
  ```ts
51
- const [github] = await client.integrations.github.list();
52
- const [googleCloud] = await client.integrations.googleCloud.list();
52
+ const connections = await client.integrations.connections.list();
53
+ const github = connections.find((c) => c.integration === "github");
54
+ const google = connections.find((c) => c.integration === "google");
53
55
 
54
56
  await client.workspaces.create({
55
57
  repo: "https://github.com/acme/product",
56
- gitIntegrationId: github.id,
57
- googleCloudIntegrationId: googleCloud.id,
58
+ connections: [github!.id, google!.id],
58
59
  });
59
60
  ```
60
61
 
@@ -117,12 +118,14 @@ without a GitTerm server, is described at the [end](#direct-provider-mode).
117
118
  `createGittermClient()` resolves its server and token in this order: constructor options →
118
119
  `GITTERM_SERVER_URL` / `GITTERM_API_TOKEN` → the CLI config at `~/.config/gitterm/cli.json`
119
120
  written by `gitterm login`. The default server is the hosted API at `https://api.gitterm.dev`;
120
- self-hosted instances use the same SDK with their own `serverUrl`:
121
+ self-hosted instances use the same SDK with their own `serverUrl`. Behind the bundled
122
+ path-routing proxy, point it at the `/api` path (for example
123
+ `GITTERM_SERVER_URL=https://<host>/api`); a server reached directly uses its origin:
121
124
 
122
125
  ```ts
123
126
  const hosted = createGittermClient({ token: process.env.GITTERM_API_TOKEN });
124
127
  const selfHosted = createGittermClient({
125
- serverUrl: "https://gitterm.example.com", // or http://localhost:3000
128
+ serverUrl: "https://gitterm.example.com/api", // or http://localhost:3000
126
129
  token: process.env.GITTERM_API_TOKEN,
127
130
  });
128
131
  const fromCli = createGittermClient(); // env vars, then the CLI's saved login
@@ -160,8 +163,16 @@ client.catalog.cloudProviders();
160
163
  client.catalog.workspaceOptions();
161
164
  client.credentials.list(); // dashboard credential metadata, never secrets
162
165
  client.credentials.listProviders();
163
- client.models.list({ provider: "openai" }); // discover provider/model IDs
164
- client.workspaces.models(workspace); // resolved sources + known models; no secrets or runtime wake-up
166
+ client.integrations.catalog(); // integrations the admin enabled: key, category, personal/shared
167
+ client.integrations.connections.list(filter?); // what you can attach; personal + shared, one shape
168
+ client.integrations.connections.get(id);
169
+ client.integrations.connections.create(input); // integrations:write; may return a browser step
170
+ client.integrations.connections.remove(id);
171
+ client.integrations.connections.waitFor({ integration, since }); // after a `pending` create
172
+ client.integrations.github.repositories(connectionId);
173
+ client.integrations.github.branches(connectionId, owner, repo);
174
+ client.integrations.google.setup(); // issuer + attribute mapping to configure Google
175
+ client.workspaces.models(workspace); // resolved credential sources; no secrets or runtime wake-up
165
176
  ```
166
177
 
167
178
  Every poll-based wait (`ensureRunning`, `waitForSetup`, and `runs.create` with `waitForSetup`)
@@ -175,19 +186,77 @@ finishes tearing down resources after the call returns.
175
186
 
176
187
  ## Workspaces
177
188
 
178
- ### Managed private repositories
189
+ ### Integrations and connections
179
190
 
180
- For renewable, short-lived repository authentication, connect the GitHub App in the GitTerm
181
- dashboard and copy its **SDK integration ID** from the Integrations page:
191
+ `client.integrations` works the same way for every provider. `catalog()` tells you which
192
+ integrations the admin has enabled and whether each allows **personal** connections (you create
193
+ them, e.g. by installing the GitHub App) and/or a **shared** one (the admin provides it for the
194
+ whole deployment). `connections.list()` returns everything you can attach right now:
182
195
 
183
196
  ```ts
184
- const { workspace, runtime } = await client.workspaces.create({
185
- repo: "https://github.com/acme/private-repo",
186
- branch: "main",
187
- gitIntegrationId: "your-dashboard-integration-id",
197
+ for (const c of await client.integrations.connections.list()) {
198
+ console.log(c.id, c.integration, c.kind, c.name, c.status);
199
+ }
200
+ // 8c1f… github personal acme-bot (GitHub App) connected
201
+ // github:shared github shared acme-ci (shared PAT) connected
202
+ // 3f7d… google personal Production connected
203
+ ```
204
+
205
+ Personal connection ids are stable row ids. Shared connection ids are well-known:
206
+ `<integration>:shared`. Pass any mix to `workspaces.create({ connections: [...] })`; the server
207
+ rejects more than one connection per integration. Credentials are issued inside the workspace and
208
+ are never returned to the SDK caller.
209
+
210
+ #### Creating connections from the SDK
211
+
212
+ Creating a connection requires an API token with the `integrations:write` scope. Some providers
213
+ complete immediately; others need a browser step and return `pending`:
214
+
215
+ ```ts
216
+ // Google Cloud: completes immediately, then tell the user what to run in gcloud.
217
+ const setup = await client.integrations.google.setup(); // issuer to configure on the provider
218
+ const created = await client.integrations.connections.create({
219
+ integration: "google",
220
+ name: "Production",
221
+ projectId: "my-project",
222
+ workloadIdentityProvider:
223
+ "projects/123456789/locations/global/workloadIdentityPools/gitterm/providers/gitterm",
224
+ serviceAccountEmail: "agent@my-project.iam.gserviceaccount.com",
188
225
  });
226
+ if (created.status === "connected") {
227
+ for (const step of created.nextSteps) console.log(step.label, "\n", step.command);
228
+ }
229
+
230
+ // GitHub App: the user installs the App in a browser, then we wait for the connection.
231
+ const started = new Date();
232
+ const pending = await client.integrations.connections.create({ integration: "github" });
233
+ if (pending.status === "pending") {
234
+ console.log("Open", pending.authorizeUrl);
235
+ const github = await client.integrations.connections.waitFor({
236
+ integration: "github",
237
+ since: started,
238
+ });
239
+ console.log("Connected as", github.name);
240
+ }
189
241
  ```
190
242
 
243
+ `connections.remove(id)` disconnects a personal connection (for GitHub it asks GitHub to uninstall
244
+ the App; the removal completes when GitHub confirms). Shared connections are managed by the admin.
245
+
246
+ #### GitHub
247
+
248
+ A GitHub App connection can browse what it has access to:
249
+
250
+ ```ts
251
+ const repos = await client.integrations.github.repositories(github.id);
252
+ const branches = await client.integrations.github.branches(github.id, "acme", "product");
253
+ ```
254
+
255
+ The admin picks one repository mode for the deployment: either users install a GitHub App
256
+ (personal connections) or the admin provides a shared PAT (`github:shared`). The shared PAT
257
+ authenticates as the admin-chosen GitHub account, so its permissions apply to every workspace that
258
+ attaches it. Neither mode requires GitHub login.
259
+
191
260
  Managed workspaces can also use dashboard-managed model subscriptions while accepting an
192
261
  application-owned GitHub PAT inline:
193
262
 
@@ -206,11 +275,10 @@ const { workspace, runtime } = await client.workspaces.create({
206
275
  });
207
276
  ```
208
277
 
209
- The username defaults to `x-access-token`. Inline repository credentials take precedence over
210
- `gitIntegrationId` and authenticate repository validation, cloning, and runtime Git operations such
211
- as pull and push. Without inline credentials, `gitIntegrationId` continues to use the connected
212
- dashboard integration. Omitting `models` likewise continues to
213
- use dashboard-managed model credentials.
278
+ The username defaults to `x-access-token`. Use either a GitHub connection in `connections` or
279
+ `repositoryCredentials`, not both. Both authenticate repository validation, cloning, and runtime
280
+ Git operations such as pull and push. Omitting `models` continues to use dashboard-managed model
281
+ credentials.
214
282
 
215
283
  #### GitHub CLI authentication
216
284
 
@@ -218,12 +286,15 @@ For GitHub repositories, GitTerm configures both Git and `gh` from the same work
218
286
  credentials. Agents can run commands such as `gh pr list` or `gh pr create` without
219
287
  running `gh auth login`:
220
288
 
221
- - `repositoryCredentials.token` takes precedence over `gitIntegrationId`. The supplied
222
- token is retained in a permission-restricted file on the workspace machine and is
223
- not automatically renewed. This also works with the standalone SDK's direct providers.
224
- - `gitIntegrationId` uses the GitHub App installation token. Git and `gh` share a
289
+ - `repositoryCredentials.token` is retained in a permission-restricted file on the workspace
290
+ machine and is not automatically renewed. This also works with the standalone SDK's direct
291
+ providers.
292
+ - A personal GitHub connection uses the GitHub App installation token. Git and `gh` share a
225
293
  cached token and refresh it through GitTerm before expiry. Concurrent commands share
226
294
  the refresh, and a failed refresh stops the command rather than using an expired token.
295
+ - The shared `github:shared` connection uses the admin's deployment-wide PAT. Switching the
296
+ deployment's GitHub mode prevents future refreshes for existing workspaces using it; revoke
297
+ the PAT at GitHub to stop access in an already-running workspace.
227
298
  - Without repository credentials or an integration, GitTerm leaves CLI authentication
228
299
  to the environment or the CLI's existing configuration.
229
300
 
@@ -253,8 +324,9 @@ Updating an integration's token does not grant permissions absent from its insta
253
324
  GitTerm does not save inline PATs in its application database. Inline PATs must be delivered to the
254
325
  selected compute provider and retained on the workspace machine for runtime Git operations, so
255
326
  provider infrastructure and processes running in that workspace may be able to access them. Prefer
256
- `gitIntegrationId` for durable managed workspaces and use narrowly scoped, short-lived PATs when
257
- inline credentials are necessary.
327
+ a GitHub connection for durable managed workspaces and use narrowly scoped, short-lived PATs when
328
+ inline credentials are necessary. The standalone/direct SDK has no GitTerm account to look up:
329
+ it supports `repositoryCredentials` but not `connections`.
258
330
 
259
331
  The SDK deliberately exposes two clients. `createGittermClient()` uses a user API token and
260
332
  can manage the user's workspaces. `createGittermWorkspaceClient()` uses the scoped identity
@@ -266,8 +338,14 @@ import { createGittermWorkspaceClient } from "@gitterm/sdk";
266
338
  const workspace = createGittermWorkspaceClient();
267
339
  const self = await workspace.self.get();
268
340
  const preview = await workspace.ports.open(3000, { name: "app" });
341
+ const api = await workspace.ports.open(8080, { name: "api", visibility: "public" });
269
342
  ```
270
343
 
344
+ Ports are `private` by default: only the workspace owner's signed-in GitTerm browser session can
345
+ reach the URL. Make a port `public` when it needs to be reachable without a GitTerm login, for
346
+ example an API or webhook receiver. Use `workspace.ports.setVisibility(port, visibility)` to change
347
+ it later.
348
+
271
349
  The workspace client never reads the CLI's saved account login and has no create, list,
272
350
  pause, restart, or terminate operations.
273
351
 
@@ -660,20 +738,21 @@ selection fails explicitly; give them distinct dashboard labels. Discovery requi
660
738
  **Inline credentials** use `{ source: "apiKey", apiKey }` for this workspace only. A missing or
661
739
  blank key fails validation; it never falls back to a saved credential. Keys are injected into
662
740
  the sandbox, not saved in the dashboard. `credentials.listProviders()` includes each authentication
663
- integration's `logicalProviderKey`. `models.list({ provider: "openai" })` discovers model IDs.
664
- Managed OAuth credentials must be connected through the dashboard.
741
+ integration's `logicalProviderKey`. Managed OAuth credentials must be connected through the
742
+ dashboard.
665
743
 
666
- `workspaces.models(workspace)` shows which sources were configured and the matching catalog models.
744
+ `workspaces.models(workspace)` shows which credential sources were configured.
667
745
  It reads control-plane metadata only: it does not verify a key with the model provider, discover
668
746
  custom runtime models, or resume a paused workspace. A saved credential's label/active status is
669
747
  its current dashboard metadata.
670
748
 
671
749
  Rules and errors:
672
750
 
673
- - Omitting `models` uses dashboard defaults. An explicit `models` block defaults to
751
+ - Omitting `models` ships every saved dashboard account, labelled as in the dashboard, with each
752
+ provider's default as the active OpenCode account. An explicit `models` block defaults to
674
753
  `inherit: "none"`; `models: {}` injects no dashboard credentials.
675
- - Use `inherit: "defaults"` to inherit every unlisted provider's dashboard default. Explicit
676
- sources override only their own provider. Each provider map key selects exactly one source.
754
+ - Use `inherit: "defaults"` to ship every unlisted provider's saved accounts the same way. Explicit
755
+ sources override only their own provider and ship exactly one account for it.
677
756
  - Unknown providers or inline keys for OAuth-only providers throw `MODEL_CREDENTIAL_INVALID`.
678
757
  A missing or ambiguous label throws `MODEL_CREDENTIAL_UNAVAILABLE`.
679
758
  - A run that requests a credential-backed `provider/model` not available in its workspace throws
@@ -683,12 +762,20 @@ Rules and errors:
683
762
  for that run. Credentials remain workspace-scoped: changing credentials for one concurrent run
684
763
  would otherwise change them for other runs on the same runtime.
685
764
 
686
- ## OpenCode API versions
765
+ ## OpenCode runtime
766
+
767
+ Every workspace runs OpenCode 2 (the `@opencode/cli` npm package) and its `/api/*` HTTP API. Runs,
768
+ questions, permissions, and events use GitTerm's OpenCode 2 runtime adapter. Attach a local TUI with
769
+ `OPENCODE_PASSWORD=<password> opencode --server <workspace url>`.
687
770
 
688
- Managed workspaces default to OpenCode V1 from `opencode-ai` and use `opencodeApi: "v1"`.
689
- Credentials are written to OpenCode's `auth.json` before the runtime starts. Runs, questions,
690
- permissions, and events use GitTerm's V1 runtime adapter. V2 remains selectable only for explicitly
691
- compatible images.
771
+ OpenCode 2 keeps credentials in its SQLite store and has no headless import, so workspaces receive
772
+ `~/.gitterm/opencode/credentials.json` and a local OpenCode plugin at
773
+ `~/.config/opencode/plugins/gitterm-credentials.js`. The plugin imports each account through OpenCode's
774
+ integration API on first load under its dashboard label (inline keys use `Gitterm`), so a provider can
775
+ carry several accounts and `opencode auth switch` works inside the workspace. The dashboard default is
776
+ the account OpenCode selects. Accounts whose label already exists on the integration are skipped, so
777
+ restarts are idempotent. OAuth entries are stored with OpenCode's built-in method IDs and refreshed by
778
+ OpenCode itself.
692
779
 
693
780
  ## Errors
694
781
 
@@ -814,10 +901,9 @@ Direct mode does not provide managed durable run storage or submission idempoten
814
901
  Serialized workspaces/runs contain runtime credentials: encrypt them and never send them to an
815
902
  untrusted UI. Custom providers can implement `DirectProviderAdapter`; inspect `client.provider.capabilities`.
816
903
 
817
- Both modes default to OpenCode **v1** and share v1/v2 run, permission, question, and SSE adapters.
818
- For direct v2, supply a compatible provider image/template and `opencode: { api: "v2" }` at workspace
819
- creation. The flag chooses the protocol; it does not install or upgrade the runtime. Direct mode
820
- does not accept saved/dashboard credential sources or `inherit: "defaults"`.
904
+ Both modes run OpenCode 2 and share its run, permission, question, and SSE adapters. Provider
905
+ images/templates must ship OpenCode 2 (`@opencode/cli`). Direct mode does not accept saved/dashboard
906
+ credential sources or `inherit: "defaults"`.
821
907
 
822
908
  Every adapter receives the same normalized plan: repository/ref and optional Git credentials, agent files, model credentials, environment, setup commands, serve command, and port. Provider-specific configuration only describes how to allocate and expose compute.
823
909
 
@@ -860,9 +946,9 @@ await direct.workspaces.create({
860
946
 
861
947
  ### Provider authentication
862
948
 
863
- Direct **v2** workspaces can start OpenCode provider authentication without shell access. This
864
- connection-management API is v2-only; v1 supports inline API keys/OAuth bundles at creation and
865
- `auth.setCredential()`. Discover a headless/device-code method for remote authentication:
949
+ Direct workspaces can start OpenCode provider authentication without shell access. Inline API
950
+ keys/OAuth bundles are still accepted at creation. Discover a headless/device-code method for remote
951
+ authentication:
866
952
 
867
953
  ```ts
868
954
  const openai = await direct.auth.get(workspace, "openai");
@@ -909,14 +995,11 @@ const workspace = await direct.workspaces.create({
909
995
  },
910
996
  });
911
997
 
912
- // v1 credentials can also be added or rotated on an existing runtime.
913
- // On v2, setCredential supports API keys; use connectOAuth for OAuth rotation.
998
+ // API keys can also be added or rotated on an existing runtime; use connectOAuth for OAuth rotation.
914
999
  await direct.auth.setCredential(workspace, {
915
- source: "oauth",
1000
+ source: "apiKey",
916
1001
  providerName: "openai",
917
- refreshToken: credential.refreshToken,
918
- accessToken: credential.accessToken,
919
- expiresAt: credential.expiresAt,
1002
+ apiKey: credential.apiKey,
920
1003
  });
921
1004
  ```
922
1005
 
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { AgentType, AgentRun, AgentRunCreateInput, AgentRunListOptions, AgentRunListResult, AgentRunMessage, AgentRunReply, AuthStatus, CloudProvider, RunRef, RunWaitOptions, RunWatchOptions, WaitOptions, Workspace, WorkspaceCreateInput, WorkspaceCreateResult, WorkspaceEnsureRunningResult, WorkspaceListOptions, WorkspaceListResult, WorkspaceRef, WorkspaceRestartResult, WorkspaceRuntimeAccess, WorkspacePauseResult, WorkspaceTerminateResult, WorkspaceCatalog, WorkspaceSetupStatus, ModelCredential, ModelProviderInfo, GitHubIntegration, GoogleCloudIntegration } from "./types.js";
2
- import type { AgentRunEvent, AgentRunResult, RunResultOptions, ModelInfo, WorkspaceModelAccess } from "./types.js";
1
+ import type { AgentType, AgentRun, AgentRunCreateInput, AgentRunListOptions, AgentRunListResult, AgentRunMessage, AgentRunReply, AuthStatus, CloudProvider, RunRef, RunWaitOptions, RunWatchOptions, WaitOptions, Workspace, WorkspaceCreateInput, WorkspaceCreateResult, WorkspaceEnsureRunningResult, WorkspaceListOptions, WorkspaceListResult, WorkspaceRef, WorkspaceRestartResult, WorkspaceRuntimeAccess, WorkspacePauseResult, WorkspaceTerminateResult, WorkspaceCatalog, WorkspaceSetupStatus, ModelCredential, ModelProviderInfo, Integration, IntegrationKey, Connection, ConnectionKind, CreateConnectionInput, CreateConnectionResult, GitHubRepository, GitHubBranch, GoogleSetup } from "./types.js";
2
+ import type { AgentRunEvent, AgentRunResult, RunResultOptions, WorkspaceModelAccess } from "./types.js";
3
3
  export type GittermClientOptions = {
4
4
  serverUrl?: string;
5
5
  token?: string;
@@ -22,7 +22,7 @@ export type GittermClient = {
22
22
  terminate(workspace: WorkspaceRef): Promise<WorkspaceTerminateResult>;
23
23
  create(input: WorkspaceCreateInput): Promise<WorkspaceCreateResult>;
24
24
  setupStatus(workspace: WorkspaceRef): Promise<WorkspaceSetupStatus>;
25
- /** Credential-source metadata and known models; never wakes the runtime. */
25
+ /** Credential-source metadata; never wakes the runtime. */
26
26
  models(workspace: WorkspaceRef): Promise<WorkspaceModelAccess>;
27
27
  waitForSetup(workspace: WorkspaceRef, options?: WaitOptions): Promise<WorkspaceSetupStatus>;
28
28
  };
@@ -70,17 +70,39 @@ export type GittermClient = {
70
70
  listProviders(): Promise<ModelProviderInfo[]>;
71
71
  };
72
72
  integrations: {
73
+ /** Integrations the admin has enabled for this deployment. */
74
+ catalog(): Promise<Integration[]>;
75
+ connections: {
76
+ /** Personal and shared connections you can attach to a workspace. */
77
+ list(filter?: {
78
+ integration?: IntegrationKey;
79
+ kind?: ConnectionKind;
80
+ }): Promise<Connection[]>;
81
+ get(id: string): Promise<Connection>;
82
+ /** Requires the `integrations:write` scope. */
83
+ create(input: CreateConnectionInput): Promise<CreateConnectionResult>;
84
+ /** Removes a personal connection. Shared connections are managed by the admin. */
85
+ remove(id: string): Promise<void>;
86
+ /**
87
+ * Poll until a connection for `integration` connected after `since` appears; use after a
88
+ * `pending` create result once the browser step is done.
89
+ */
90
+ waitFor(options: {
91
+ integration: IntegrationKey;
92
+ since?: Date | string;
93
+ timeoutMs?: number;
94
+ intervalMs?: number;
95
+ }): Promise<Connection>;
96
+ };
73
97
  github: {
74
- list(): Promise<GitHubIntegration[]>;
98
+ /** Repositories reachable through a GitHub App connection. */
99
+ repositories(connectionId: string): Promise<GitHubRepository[]>;
100
+ branches(connectionId: string, owner: string, repo: string): Promise<GitHubBranch[]>;
75
101
  };
76
- googleCloud: {
77
- list(): Promise<GoogleCloudIntegration[]>;
102
+ google: {
103
+ /** Issuer and attribute mapping to configure the Google provider before connecting. */
104
+ setup(): Promise<GoogleSetup>;
78
105
  };
79
106
  };
80
- models: {
81
- list(options?: {
82
- provider?: string;
83
- }): Promise<ModelInfo[]>;
84
- };
85
107
  };
86
108
  export declare function createGittermClient(options?: GittermClientOptions): GittermClient;