@gitterm/sdk 0.0.8 → 0.1.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 +183 -8
- package/dist/direct/ascii.d.ts +2 -0
- package/dist/direct/client.d.ts +60 -0
- package/dist/direct/daytona.d.ts +2 -0
- package/dist/direct/e2b.d.ts +2 -0
- package/dist/direct/exedev.d.ts +2 -0
- package/dist/direct/index.d.ts +8 -0
- package/dist/direct/index.js +2026 -0
- package/dist/direct/provisioning.d.ts +39 -0
- package/dist/direct/railway.d.ts +2 -0
- package/dist/direct/types.d.ts +319 -0
- package/dist/direct/vercel.d.ts +2 -0
- package/dist/index.js +12 -12
- package/dist/types.d.ts +17 -4
- package/package.json +15 -4
package/README.md
CHANGED
|
@@ -4,6 +4,152 @@ TypeScript SDK for the [GitTerm](https://gitterm.dev) API. Used by the `gitterm`
|
|
|
4
4
|
the OpenCode plugin, and any integration that needs to manage GitTerm workspaces with
|
|
5
5
|
a user API token.
|
|
6
6
|
|
|
7
|
+
## Direct provider mode
|
|
8
|
+
|
|
9
|
+
Direct mode runs an agent using your cloud-provider account without a Gitterm server. It intentionally omits managed billing, proxying, policy, durable run history, and automatic cleanup; your application owns workspace state and lifecycle.
|
|
10
|
+
|
|
11
|
+
All built-in compute providers use the same provisioning plan and workspace/run API:
|
|
12
|
+
|
|
13
|
+
| Provider | Direct prerequisite | Persistent pause | Keep-alive |
|
|
14
|
+
| -------- | -------------------------------------------------------------- | ---------------- | ---------- |
|
|
15
|
+
| E2B | OpenCode-compatible template | Yes | Yes |
|
|
16
|
+
| Daytona | Public Gitterm OpenCode server image by default | Yes | Yes |
|
|
17
|
+
| Vercel | Vercel Sandbox project | Yes | Yes |
|
|
18
|
+
| Ascii | Box API key | Yes | Yes |
|
|
19
|
+
| exe.dev | Lifecycle token, or an existing VM with `ls,ssh,share,ssh-key` | Yes | No |
|
|
20
|
+
| Railway | Project/environment and public service domains | With a volume | No |
|
|
21
|
+
|
|
22
|
+
AWS remains available through `createGittermClient()` and the Gitterm control plane; it is intentionally not exposed in direct mode.
|
|
23
|
+
|
|
24
|
+
Cloudflare remains available through the Gitterm control plane. Direct Cloudflare support is deferred until the OpenCode v2 Workerd runtime is stable.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { createDirectGittermClient } from "@gitterm/sdk/direct";
|
|
28
|
+
|
|
29
|
+
const direct = createDirectGittermClient({
|
|
30
|
+
provider: {
|
|
31
|
+
type: "e2b",
|
|
32
|
+
apiKey: process.env.E2B_API_KEY!,
|
|
33
|
+
size: "standard",
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
let workspace = await direct.workspaces.create({
|
|
38
|
+
repo: "https://github.com/acme/project",
|
|
39
|
+
lifecycle: "ephemeral",
|
|
40
|
+
modelCredentials: [{ providerName: "anthropic", apiKey: process.env.ANTHROPIC_API_KEY! }],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const run = await direct.runs.create({ workspace, prompt: "Review the open pull request" });
|
|
45
|
+
const completed = await direct.runs.wait(run, workspace);
|
|
46
|
+
console.log(completed.finalText);
|
|
47
|
+
} finally {
|
|
48
|
+
workspace = await direct.workspaces.terminate(workspace);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`DirectWorkspace` is JSON-serializable. Persist it together with the returned `sessionId` to resume provider lifecycle and OpenCode conversation context after an application restart. The serialized workspace contains the OpenCode password and may contain provider routing tokens, so encrypt it as credential material. Custom providers can implement `DirectProviderAdapter`; use `client.provider.capabilities` rather than hard-coding lifecycle assumptions.
|
|
53
|
+
|
|
54
|
+
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.
|
|
55
|
+
|
|
56
|
+
Direct setup has explicit phases. `beforeAgent` blocks workspace creation, while
|
|
57
|
+
`afterAgent` runs in the background and can be observed with `setupStatus()` or
|
|
58
|
+
`waitForSetup()`:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
const workspace = await direct.workspaces.create({
|
|
62
|
+
repo: "https://github.com/acme/project",
|
|
63
|
+
setup: {
|
|
64
|
+
beforeAgent: ["npm install"],
|
|
65
|
+
afterAgent: ["npm run generate"],
|
|
66
|
+
},
|
|
67
|
+
secretFiles: [
|
|
68
|
+
{
|
|
69
|
+
path: "~/.config/gcloud/service-account.json",
|
|
70
|
+
content: process.env.GCP_SERVICE_ACCOUNT_JSON!,
|
|
71
|
+
mode: 0o600,
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
await direct.workspaces.waitForSetup(workspace);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
To attach to an existing exe.dev VM without giving Gitterm ownership of that VM, pass
|
|
80
|
+
`exedev: { existingVmName: "acme-agent-machine" }` to `workspaces.create()`. Terminating
|
|
81
|
+
that workspace stops only its tracked agent process and does not remove the VM.
|
|
82
|
+
|
|
83
|
+
Trusted integration context can be appended to the generated global `AGENTS.md` without changing the model system prompt:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
await direct.workspaces.create({
|
|
87
|
+
repo: "https://github.com/acme/project",
|
|
88
|
+
additionalAgentInstructions:
|
|
89
|
+
"You are running as a Slack bot. Keep responses concise and suitable for a thread.",
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Provider authentication
|
|
94
|
+
|
|
95
|
+
Direct workspaces can start OpenCode provider authentication without shell access. Discover the provider's methods and select a headless or device-code OAuth method when OpenCode is running remotely:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
const openai = await direct.auth.get(workspace, "openai");
|
|
99
|
+
const method = openai.methods.find(
|
|
100
|
+
(item) => item.type === "oauth" && item.id === "chatgpt-headless",
|
|
101
|
+
);
|
|
102
|
+
if (!method || method.type !== "oauth") throw new Error("OpenAI device OAuth is unavailable");
|
|
103
|
+
|
|
104
|
+
const attempt = await direct.auth.connectOAuth({
|
|
105
|
+
workspace,
|
|
106
|
+
integrationId: "openai",
|
|
107
|
+
methodId: method.id,
|
|
108
|
+
label: "Slack bot",
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Present these through your application UI.
|
|
112
|
+
console.log(attempt.url, attempt.instructions);
|
|
113
|
+
|
|
114
|
+
if (attempt.mode === "auto") {
|
|
115
|
+
await direct.auth.wait(attempt, workspace);
|
|
116
|
+
} else {
|
|
117
|
+
await direct.auth.complete(attempt, workspace, await getCodeFromUser());
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
OAuth started this way is stored and refreshed by OpenCode inside the workspace. Reusing a persistent workspace avoids repeated authentication; terminating an ephemeral workspace also destroys its credential store. OpenCode does not export OAuth tokens from this flow.
|
|
122
|
+
|
|
123
|
+
Applications that own OAuth separately can keep the token bundle in encrypted storage and inject it into every new workspace instead:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
const credential = await credentialStore.get(slackInstallationId);
|
|
127
|
+
const workspace = await direct.workspaces.create({
|
|
128
|
+
lifecycle: "ephemeral",
|
|
129
|
+
modelCredentials: [
|
|
130
|
+
{
|
|
131
|
+
type: "oauth",
|
|
132
|
+
providerName: "openai",
|
|
133
|
+
refreshToken: credential.refreshToken,
|
|
134
|
+
accessToken: credential.accessToken,
|
|
135
|
+
expiresAt: credential.expiresAt,
|
|
136
|
+
accountId: credential.accountId,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Credentials can also be added or rotated on an existing runtime.
|
|
142
|
+
await direct.auth.setCredential(workspace, {
|
|
143
|
+
type: "oauth",
|
|
144
|
+
providerName: "openai",
|
|
145
|
+
refreshToken: credential.refreshToken,
|
|
146
|
+
accessToken: credential.accessToken,
|
|
147
|
+
expiresAt: credential.expiresAt,
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
In this mode the application owns encryption, tenant scoping, refresh, and persistence. OpenCode may refresh its workspace-local copy; the direct SDK does not copy rotated tokens back into application storage. Use the Gitterm control plane when those credential-management responsibilities should be managed centrally.
|
|
152
|
+
|
|
7
153
|
## Install
|
|
8
154
|
|
|
9
155
|
```sh
|
|
@@ -128,7 +274,10 @@ Override only the placement decisions your integration cares about:
|
|
|
128
274
|
await client.workspaces.create({
|
|
129
275
|
repo: "https://github.com/acme/product",
|
|
130
276
|
agent: "opencode",
|
|
131
|
-
|
|
277
|
+
setup: {
|
|
278
|
+
beforeAgent: ["npm install"],
|
|
279
|
+
afterAgent: ["npm run generate"],
|
|
280
|
+
},
|
|
132
281
|
opencode: {
|
|
133
282
|
skills: [
|
|
134
283
|
{
|
|
@@ -150,12 +299,38 @@ Follow the repository's release-demo workflow.`,
|
|
|
150
299
|
});
|
|
151
300
|
```
|
|
152
301
|
|
|
153
|
-
Setup commands run in order from the checked-out repository
|
|
154
|
-
|
|
155
|
-
agent
|
|
156
|
-
`client.workspaces.setupStatus(workspaceId)`
|
|
157
|
-
|
|
158
|
-
repository's git-excluded `.gitterm/setup/` directory.
|
|
302
|
+
Setup commands run in order from the checked-out repository. `beforeAgent` blocks agent
|
|
303
|
+
startup; when it fails, `create()` rejects with the tail of its log. `afterAgent` starts
|
|
304
|
+
after the agent is reachable and reports status independently. Provider and agent defaults
|
|
305
|
+
configured by an administrator run first. Use `client.workspaces.setupStatus(workspaceId)`
|
|
306
|
+
or `waitForSetup(workspaceId)` to inspect the `afterAgent` phase. GitTerm persists bounded
|
|
307
|
+
logs and a recovery copy in the repository's git-excluded `.gitterm/setup/` directory.
|
|
308
|
+
Setup commands can reference the checkout with `$WORKSPACE_REPO_DIR`, which is the same on
|
|
309
|
+
every provider even though the underlying path differs.
|
|
310
|
+
|
|
311
|
+
Secret files are created relative to the repository with restrictive permissions and are
|
|
312
|
+
added to `.git/info/exclude` so the agent cannot commit them. GitTerm does not retain their
|
|
313
|
+
contents; to rotate a secret, recreate the workspace. Like model credentials, they are
|
|
314
|
+
delivered to the sandbox through its launch environment, so anyone who can read the
|
|
315
|
+
provider's task or container definition can read them:
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
await client.workspaces.create({
|
|
319
|
+
repo: "https://github.com/acme/product",
|
|
320
|
+
secretFiles: [
|
|
321
|
+
{
|
|
322
|
+
path: ".secrets/gcp.json",
|
|
323
|
+
content: process.env.GCP_SERVICE_ACCOUNT_JSON!,
|
|
324
|
+
mode: "0600",
|
|
325
|
+
},
|
|
326
|
+
],
|
|
327
|
+
setup: {
|
|
328
|
+
beforeAgent: [
|
|
329
|
+
'gcloud auth activate-service-account --key-file "$WORKSPACE_REPO_DIR/.secrets/gcp.json"',
|
|
330
|
+
],
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
```
|
|
159
334
|
|
|
160
335
|
`provider` is a discriminated union, so TypeScript only offers `region` for providers
|
|
161
336
|
where GitTerm supports caller-selected placement. Machine keys are configured by admins
|
|
@@ -177,7 +352,7 @@ it does not claim that a pull request, upload, or other product outcome succeede
|
|
|
177
352
|
```ts
|
|
178
353
|
const { workspace } = await client.workspaces.create({
|
|
179
354
|
repo: "https://github.com/acme/product",
|
|
180
|
-
|
|
355
|
+
setup: { afterAgent: ["npm install", "npm run db:seed"] },
|
|
181
356
|
});
|
|
182
357
|
|
|
183
358
|
const run = await client.runs.create({
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { DirectProviderAdapter, DirectProviderConfig, DirectAuthAttempt, DirectAuthAttemptStatus, DirectAuthIntegration, DirectAuthWaitOptions, DirectModelCredential, DirectRun, DirectRunCreateInput, DirectRunMessage, DirectRunWaitOptions, DirectWorkspace, DirectWorkspaceCreateInput, DirectWorkspaceSetupStatus, DirectWorkspaceSetupWaitOptions } from "./types.js";
|
|
2
|
+
export type DirectGittermClientOptions = {
|
|
3
|
+
provider: DirectProviderAdapter | DirectProviderConfig;
|
|
4
|
+
};
|
|
5
|
+
export declare function createDirectGittermClient(options: DirectGittermClientOptions): {
|
|
6
|
+
provider: {
|
|
7
|
+
name: string;
|
|
8
|
+
capabilities: import("./types.js").DirectProviderCapabilities;
|
|
9
|
+
};
|
|
10
|
+
auth: {
|
|
11
|
+
setCredential(workspace: DirectWorkspace, credential: DirectModelCredential): Promise<void>;
|
|
12
|
+
list(workspace: DirectWorkspace): Promise<DirectAuthIntegration[]>;
|
|
13
|
+
get(workspace: DirectWorkspace, integrationId: string): Promise<DirectAuthIntegration>;
|
|
14
|
+
connectKey(input: {
|
|
15
|
+
workspace: DirectWorkspace;
|
|
16
|
+
integrationId: string;
|
|
17
|
+
key: string;
|
|
18
|
+
label?: string;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
connectOAuth(input: {
|
|
21
|
+
workspace: DirectWorkspace;
|
|
22
|
+
integrationId: string;
|
|
23
|
+
methodId: string;
|
|
24
|
+
inputs?: Record<string, string>;
|
|
25
|
+
label?: string;
|
|
26
|
+
}): Promise<DirectAuthAttempt>;
|
|
27
|
+
status(attempt: DirectAuthAttempt, workspace: DirectWorkspace): Promise<DirectAuthAttemptStatus>;
|
|
28
|
+
complete(attempt: DirectAuthAttempt, workspace: DirectWorkspace, code: string): Promise<void>;
|
|
29
|
+
wait(attempt: DirectAuthAttempt, workspace: DirectWorkspace, wait?: DirectAuthWaitOptions): Promise<DirectAuthAttemptStatus>;
|
|
30
|
+
cancel(attempt: DirectAuthAttempt, workspace: DirectWorkspace): Promise<void>;
|
|
31
|
+
};
|
|
32
|
+
workspaces: {
|
|
33
|
+
create(input?: DirectWorkspaceCreateInput): Promise<DirectWorkspace>;
|
|
34
|
+
status(workspace: DirectWorkspace): Promise<DirectWorkspace>;
|
|
35
|
+
pause(workspace: DirectWorkspace): Promise<DirectWorkspace>;
|
|
36
|
+
resume(workspace: DirectWorkspace): Promise<DirectWorkspace>;
|
|
37
|
+
terminate(workspace: DirectWorkspace): Promise<DirectWorkspace>;
|
|
38
|
+
keepAlive(workspace: DirectWorkspace, timeoutMs: number): Promise<void>;
|
|
39
|
+
setupStatus: (workspace: DirectWorkspace) => Promise<DirectWorkspaceSetupStatus>;
|
|
40
|
+
waitForSetup(workspace: DirectWorkspace, wait?: DirectWorkspaceSetupWaitOptions): Promise<DirectWorkspaceSetupStatus>;
|
|
41
|
+
};
|
|
42
|
+
runs: {
|
|
43
|
+
create(input: DirectRunCreateInput): Promise<DirectRun>;
|
|
44
|
+
get: (run: DirectRun, workspace: DirectWorkspace) => Promise<{
|
|
45
|
+
status: "running" | "completed" | "failed" | "cancelled" | "retrying";
|
|
46
|
+
error: string | null;
|
|
47
|
+
finalText: string | null;
|
|
48
|
+
id: string;
|
|
49
|
+
workspaceId: string;
|
|
50
|
+
sessionId: string;
|
|
51
|
+
messageId: string;
|
|
52
|
+
title: string;
|
|
53
|
+
submittedAt: string;
|
|
54
|
+
}>;
|
|
55
|
+
wait(run: DirectRun, workspace: DirectWorkspace, wait?: DirectRunWaitOptions): Promise<DirectRun>;
|
|
56
|
+
messages(run: DirectRun, workspace: DirectWorkspace): Promise<DirectRunMessage[]>;
|
|
57
|
+
cancel(run: DirectRun, workspace: DirectWorkspace): Promise<boolean>;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
export type DirectGittermClient = ReturnType<typeof createDirectGittermClient>;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { createDirectGittermClient, type DirectGittermClient, type DirectGittermClientOptions, } from "./client.js";
|
|
2
|
+
export { createE2BDirectProvider } from "./e2b.js";
|
|
3
|
+
export { createAsciiDirectProvider } from "./ascii.js";
|
|
4
|
+
export { createDaytonaDirectProvider } from "./daytona.js";
|
|
5
|
+
export { createExeDevDirectProvider } from "./exedev.js";
|
|
6
|
+
export { createRailwayDirectProvider } from "./railway.js";
|
|
7
|
+
export { createVercelDirectProvider } from "./vercel.js";
|
|
8
|
+
export type { AsciiDirectProviderConfig, DirectAuthAttempt, DirectAuthAttemptStatus, DirectAuthIntegration, DirectAuthMethod, DirectAuthPrompt, DirectAuthWaitOptions, DirectApiModelCredential, DaytonaDirectProviderConfig, DirectModelCredential, DirectOAuthModelCredential, DirectProviderAdapter, DirectProviderCapabilities, DirectProviderConfig, DirectProviderWorkspaceInput, DirectRun, DirectRunCreateInput, DirectRunMessage, DirectRunWaitOptions, DirectSecretFile, DirectWorkspace, DirectWorkspaceCreateInput, DirectWorkspaceLifecycle, DirectWorkspaceRuntime, DirectWorkspaceSetup, DirectWorkspaceSetupStatus, DirectWorkspaceSetupWaitOptions, E2BDirectProviderConfig, ExeDevDirectProviderConfig, RailwayDirectProviderConfig, VercelDirectProviderConfig, } from "./types.js";
|