@gitterm/sdk 0.11.0 → 0.12.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.
@@ -0,0 +1,41 @@
1
+ import type { SandboxBackend } from "./backend.js";
2
+ /**
3
+ * Durable jobs. State lives in the sandbox (`~/.gitterm/jobs/<id>`), so any process holding
4
+ * the sandbox reference can reattach: a plugin restart loses nothing.
5
+ *
6
+ * The command runs in its own session/process group. Cancellation and the timeout
7
+ * signal that whole group, and status reports whether the group is actually alive.
8
+ */
9
+ export declare const jobRoot: (home: string) => string;
10
+ export type JobStatus = "running" | "succeeded" | "failed" | "timed_out" | "cancelled" | "lost";
11
+ export type JobState = {
12
+ id: string;
13
+ status: JobStatus;
14
+ /** The command's exit code. A non-zero code is a command failure, not a provider failure. */
15
+ exitCode: number | null;
16
+ startedAt: string | null;
17
+ finishedAt: string | null;
18
+ /** Whether any process of the job's group is still running in the sandbox. */
19
+ processAlive: boolean;
20
+ logBytes: number;
21
+ };
22
+ export type JobStartInput = {
23
+ command: string;
24
+ /** Absolute directory in the sandbox. */
25
+ cwd: string;
26
+ timeoutMs: number;
27
+ env?: Record<string, string>;
28
+ };
29
+ export declare function newJobId(): string;
30
+ /**
31
+ * Secret values are redacted line by line inside the sandbox, so a value spanning lines
32
+ * (a PEM key, say) or one too short to match safely cannot be protected: refuse it before
33
+ * anything is sent.
34
+ */
35
+ export declare function assertRedactableSecrets(env?: Record<string, string>): void;
36
+ export declare function startJob(backend: SandboxBackend, sandboxId: string, id: string, input: JobStartInput): Promise<void>;
37
+ export declare function readJobState(backend: SandboxBackend, sandboxId: string, id: string): Promise<JobState | null>;
38
+ /** Reads `limit` bytes of combined stdout/stderr starting at `offset`. */
39
+ export declare function readJobLog(backend: SandboxBackend, sandboxId: string, id: string, offset: number, limit: number): Promise<Buffer>;
40
+ /** Signals the job's process group: TERM, then KILL after `graceSeconds`. */
41
+ export declare function cancelJob(backend: SandboxBackend, sandboxId: string, id: string, graceSeconds?: number): Promise<void>;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * A loopback-only proxy for a private preview. Local tools (for example browser
3
+ * automation the user approved) open `url`; the proxy adds the sandbox access headers, so
4
+ * they are never shown to a model or pasted into a browser.
5
+ *
6
+ * Each preview is served from its own hostname, `p-<random>.localhost`, which browsers and
7
+ * Node resolve to loopback. Cookies are scoped by host (not port), so a separate hostname
8
+ * keeps the preview's cookies, and its JavaScript, apart from every other local app and
9
+ * other previews, while the app's own cookie names keep working. Paths start at `/`, so
10
+ * root-relative assets and client-side routes work. The capability token in `url` is
11
+ * exchanged for an HttpOnly cookie on first use; requests without it, or for any other
12
+ * Host, are refused. The proxy lives only as long as this process. No WebSockets.
13
+ */
14
+ export type PreviewProxy = {
15
+ url: string;
16
+ close(): Promise<void>;
17
+ };
18
+ export declare function startPreviewProxy(access: {
19
+ url: string;
20
+ headers: Record<string, string>;
21
+ }): Promise<PreviewProxy>;
@@ -0,0 +1,38 @@
1
+ import type { SandboxIdentity } from "./backend.js";
2
+ /**
3
+ * A setup snapshot is a provider snapshot of a sandbox taken after setup work (dependencies,
4
+ * toolchains, services), so new sandboxes start ready instead of redoing it. An account can
5
+ * keep several; each may be tied to the checkout it was made for. Records live locally next
6
+ * to the sandbox records and never hold credentials.
7
+ */
8
+ export type SetupSnapshotRecord = SandboxIdentity & {
9
+ /** The provider's snapshot id; new sandboxes start from it. */
10
+ snapshotId: string;
11
+ /** A short label, e.g. "deps" or "deps + postgres". */
12
+ label: string;
13
+ /** The checkout it was made for, or null when it serves any project. */
14
+ project: string | null;
15
+ /** What the sandbox has set up, in the agent's own words. */
16
+ note: string;
17
+ /** Hashes of the checkout's dependency files when saved; a change means it is out of date. */
18
+ fingerprint: Record<string, string>;
19
+ sandboxId: string;
20
+ createdAt: string;
21
+ };
22
+ export type SetupSnapshotStatus = {
23
+ record: SetupSnapshotRecord;
24
+ /** Dependency files added, changed, or removed since; empty for untied snapshots. */
25
+ changed: string[];
26
+ };
27
+ /** Hashes of the setup files present at the checkout root. */
28
+ export declare function setupFingerprint(project: string): Promise<Record<string, string>>;
29
+ export declare function changedSetupFiles(saved: Record<string, string>, current: Record<string, string>): string[];
30
+ /** A provider-safe snapshot name that says what it is and, if tied, which checkout. */
31
+ export declare function setupSnapshotName(label: string, project: string | null): string;
32
+ export interface SetupSnapshotStore {
33
+ /** This account's snapshots, newest first. */
34
+ list(identity: SandboxIdentity): Promise<SetupSnapshotRecord[]>;
35
+ put(record: SetupSnapshotRecord): Promise<void>;
36
+ remove(identity: SandboxIdentity, snapshotId: string): Promise<void>;
37
+ }
38
+ export declare function createFileSetupSnapshotStore(directory?: string): SetupSnapshotStore;
@@ -0,0 +1,57 @@
1
+ import type { SandboxBackend } from "./backend.js";
2
+ /**
3
+ * Source snapshots for sandbox builds: the working tree exactly as it is on disk
4
+ * (committed, staged, unstaged, and non-ignored untracked files; deleted files absent),
5
+ * without Git history or the conversation. Secret-looking files are excluded.
6
+ */
7
+ /** The synced checkout and Gitterm bookkeeping live below the sandbox user's home. */
8
+ export declare const projectDir: (home: string) => string;
9
+ export declare const MAX_SOURCE_BYTES: number;
10
+ export type SnapshotFile = {
11
+ path: string;
12
+ kind: "file" | "executable" | "symlink";
13
+ size: number;
14
+ /** sha256 of the content, or of the link target for symlinks. */
15
+ sha256: string;
16
+ };
17
+ export type SourceSnapshot = {
18
+ id: string;
19
+ /** sha256 over the sorted manifest. Identifies exactly what runs remotely. */
20
+ digest: string;
21
+ root: string;
22
+ head: string | null;
23
+ files: SnapshotFile[];
24
+ /** Tracked files deleted in the working tree; they are absent from the snapshot. */
25
+ deleted: string[];
26
+ /** Tracked files with uncommitted changes. */
27
+ modified: string[];
28
+ /** Non-ignored untracked files included in the snapshot. */
29
+ untracked: string[];
30
+ excluded: Array<{
31
+ path: string;
32
+ reason: string;
33
+ }>;
34
+ bytes: number;
35
+ };
36
+ export type CapturedSource = {
37
+ snapshot: SourceSnapshot;
38
+ archive: Buffer;
39
+ };
40
+ export declare function safeRelativePath(file: string): boolean;
41
+ export declare function sensitivePath(file: string): boolean;
42
+ /** `*` stays within a path segment, `**` crosses segments. A pattern without `/` matches any basename. */
43
+ export declare function globToRegExp(glob: string): RegExp;
44
+ export declare function captureSource(directory: string, options?: {
45
+ exclude?: string[];
46
+ maxBytes?: number;
47
+ }): Promise<CapturedSource>;
48
+ /**
49
+ * Makes the sandbox project directory match the snapshot: files from the previous
50
+ * snapshot that are absent now are deleted, every snapshot file is written, and each
51
+ * regular file is checked against its sha256 before success is reported. Ignored build
52
+ * output (e.g. node_modules) that was never part of a snapshot is left in place.
53
+ */
54
+ export declare function uploadSource(backend: SandboxBackend, sandboxId: string, captured: CapturedSource): Promise<{
55
+ verified: true;
56
+ digest: string;
57
+ }>;
@@ -0,0 +1,78 @@
1
+ import type { SandboxRef, SandboxState } from "./backend.js";
2
+ import type { JobStatus } from "./jobs.js";
3
+ import type { SourceSnapshot } from "./source.js";
4
+ /**
5
+ * Local, non-secret bookkeeping shared by the CLI and the OpenCode plugin. It never
6
+ * holds credentials, preview access headers, file contents, or command output.
7
+ */
8
+ export type JobRecord = {
9
+ id: string;
10
+ command: string;
11
+ cwd: string;
12
+ snapshotId: string | null;
13
+ snapshotDigest: string | null;
14
+ /** Checkout the snapshot was captured from; together with `snapshotId` it names the manifest. */
15
+ snapshotRoot?: string | null;
16
+ status: JobStatus;
17
+ exitCode: number | null;
18
+ startedAt: string | null;
19
+ finishedAt: string | null;
20
+ timeoutMs: number;
21
+ /** Names (never values) of variables passed to the job. */
22
+ secretNames: string[];
23
+ /** The conversation that started the job, told when it finishes. */
24
+ sessionId?: string | null;
25
+ };
26
+ /** A downloaded file and the job (and therefore snapshot) that produced it. */
27
+ export type ArtifactRecord = {
28
+ localPath: string;
29
+ remotePath: string;
30
+ sha256: string;
31
+ bytes: number;
32
+ jobId: string;
33
+ fetchedAt: string;
34
+ };
35
+ export type SandboxRecord = SandboxRef & {
36
+ name: string;
37
+ purpose: string;
38
+ /** Local checkout the sandbox serves, when known. */
39
+ project: string | null;
40
+ sessionId: string | null;
41
+ state: SandboxState;
42
+ publicPreviews: boolean;
43
+ createdAt: string;
44
+ expiresAt: string | null;
45
+ snapshot: {
46
+ id: string;
47
+ digest: string;
48
+ root?: string;
49
+ syncedAt: string;
50
+ } | null;
51
+ jobs: Record<string, JobRecord>;
52
+ artifacts?: Record<string, ArtifactRecord>;
53
+ previews: Record<string, {
54
+ port: number;
55
+ jobId: string | null;
56
+ url: string;
57
+ public: boolean;
58
+ }>;
59
+ updatedAt: string;
60
+ };
61
+ export interface SandboxStore {
62
+ list(): Promise<SandboxRecord[]>;
63
+ get(ref: SandboxRef): Promise<SandboxRecord | null>;
64
+ put(record: SandboxRecord): Promise<void>;
65
+ /**
66
+ * Read-modify-write under a lock shared by every process using this directory, so the
67
+ * CLI and the plugin never drop each other's jobs. `create` supplies a record when none
68
+ * is stored; without one, nothing is written and null is returned.
69
+ */
70
+ update(ref: SandboxRef, mutate: (record: SandboxRecord) => void, create?: () => SandboxRecord | null): Promise<SandboxRecord | null>;
71
+ remove(ref: SandboxRef): Promise<void>;
72
+ /** Manifests (paths and hashes, no contents) for stale checks when applying artifacts. */
73
+ putSnapshot(snapshot: SourceSnapshot): Promise<void>;
74
+ /** Snapshot ids describe content only; the checkout root selects the right manifest. */
75
+ getSnapshot(id: string, root: string): Promise<SourceSnapshot | null>;
76
+ }
77
+ export declare function defaultStoreDirectory(): string;
78
+ export declare function createFileSandboxStore(directory?: string): SandboxStore;
@@ -0,0 +1,100 @@
1
+ // src/sandbox/store.ts
2
+ import { createHash, randomBytes } from "node:crypto";
3
+ import { mkdir, readdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
4
+ import { homedir } from "node:os";
5
+ import path from "node:path";
6
+ function defaultStoreDirectory() {
7
+ const base = process.env.XDG_STATE_HOME || path.join(homedir(), ".local", "state");
8
+ return path.join(base, "gitterm", "sandboxes");
9
+ }
10
+ var fileName = (ref) => `${ref.mode}-${ref.provider}-${ref.account}-${ref.id}`.replace(/[^A-Za-z0-9_.-]/g, "_") + ".json";
11
+ var snapshotFile = (id, root) => `${id.replace(/[^A-Za-z0-9_]/g, "")}-${createHash("sha256").update(root).digest("hex").slice(0, 16)}.json`;
12
+ var LOCK_STALE_MS = 30000;
13
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
14
+ async function withFileLock(target, action) {
15
+ const lock = `${target}.lock`;
16
+ const deadline = Date.now() + 2 * LOCK_STALE_MS;
17
+ while (true) {
18
+ try {
19
+ await writeFile(lock, String(process.pid), { flag: "wx", mode: 384 });
20
+ break;
21
+ } catch (error) {
22
+ if (error.code !== "EEXIST")
23
+ throw error;
24
+ const age = await stat(lock).then((info) => Date.now() - info.mtimeMs, () => 0);
25
+ if (age > LOCK_STALE_MS)
26
+ await rm(lock, { force: true });
27
+ else if (Date.now() > deadline)
28
+ throw new Error(`Timed out waiting for ${lock}`);
29
+ else
30
+ await sleep(5 + Math.random() * 20);
31
+ }
32
+ }
33
+ try {
34
+ return await action();
35
+ } finally {
36
+ await rm(lock, { force: true });
37
+ }
38
+ }
39
+ function createFileSandboxStore(directory = defaultStoreDirectory()) {
40
+ const read = async (file) => JSON.parse(await readFile(path.join(directory, file), "utf8"));
41
+ const write = async (target, record) => {
42
+ const temporary = `${target}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
43
+ try {
44
+ await writeFile(temporary, JSON.stringify(record, null, 2), { mode: 384 });
45
+ await rename(temporary, target);
46
+ } finally {
47
+ await rm(temporary, { force: true });
48
+ }
49
+ };
50
+ const queues = new Map;
51
+ const serialized = (target, action) => {
52
+ const next = (queues.get(target) ?? Promise.resolve()).catch(() => {}).then(() => withFileLock(target, action));
53
+ queues.set(target, next);
54
+ next.then(() => queues.get(target) === next && queues.delete(target), () => queues.get(target) === next && queues.delete(target));
55
+ return next;
56
+ };
57
+ return {
58
+ async list() {
59
+ const files = await readdir(directory).catch(() => []);
60
+ const records = await Promise.all(files.filter((file) => file.endsWith(".json")).map((file) => read(file).catch(() => null)));
61
+ return records.filter((record) => record !== null);
62
+ },
63
+ async get(ref) {
64
+ return read(fileName(ref)).catch(() => null);
65
+ },
66
+ async put(record) {
67
+ await mkdir(directory, { recursive: true, mode: 448 });
68
+ const target = path.join(directory, fileName(record));
69
+ await serialized(target, () => write(target, record));
70
+ },
71
+ async update(ref, mutate, create) {
72
+ await mkdir(directory, { recursive: true, mode: 448 });
73
+ const target = path.join(directory, fileName(ref));
74
+ return serialized(target, async () => {
75
+ const current = await read(fileName(ref)).catch(() => null) ?? create?.() ?? null;
76
+ if (!current)
77
+ return null;
78
+ mutate(current);
79
+ current.updatedAt = new Date().toISOString();
80
+ await write(target, current);
81
+ return current;
82
+ });
83
+ },
84
+ async remove(ref) {
85
+ await rm(path.join(directory, fileName(ref)), { force: true });
86
+ },
87
+ async putSnapshot(snapshot) {
88
+ const target = path.join(directory, "snapshots", snapshotFile(snapshot.id, snapshot.root));
89
+ await mkdir(path.dirname(target), { recursive: true, mode: 448 });
90
+ await writeFile(target, JSON.stringify(snapshot), { mode: 384 });
91
+ },
92
+ async getSnapshot(id, root) {
93
+ return readFile(path.join(directory, "snapshots", snapshotFile(id, root)), "utf8").then((text) => JSON.parse(text)).catch(() => null);
94
+ }
95
+ };
96
+ }
97
+ export {
98
+ createFileSandboxStore,
99
+ defaultStoreDirectory
100
+ };
@@ -0,0 +1,12 @@
1
+ /** A minimal POSIX (pax) tar writer, so archives are byte-identical on every platform. */
2
+ export type TarEntry = {
3
+ path: string;
4
+ type: "file";
5
+ data: Uint8Array;
6
+ executable: boolean;
7
+ } | {
8
+ path: string;
9
+ type: "symlink";
10
+ target: string;
11
+ };
12
+ export declare function createTarGz(entries: TarEntry[]): Buffer;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Public sandbox types. The provider implementations live in a private package that the
3
+ * SDK bundles, so published declarations must not reference it; these mirror its shapes
4
+ * and the assertions below keep the two in step.
5
+ */
6
+ export type SandboxProviderKey = "e2b" | "daytona" | "vercel" | "boat" | "exedev" | "railway" | "aws" | "gitterm";
7
+ export type SandboxCapabilities = {
8
+ /**
9
+ * How an abandoned sandbox ends without the caller: `hard` deleted at the lifetime even
10
+ * while busy; `idle` deleted after a period without API activity; `archive` stopped and
11
+ * archived at the lifetime with storage kept until deleted; `none` never.
12
+ */
13
+ expiry: "hard" | "idle" | "archive" | "none";
14
+ /**
15
+ * `private` needs the access header, `public` is open to anyone with the URL, `either` is
16
+ * private unless created with public previews.
17
+ */
18
+ previews: "private" | "public" | "either" | "none";
19
+ pause: "stateful" | "state-losing";
20
+ maxLifetimeMs: number;
21
+ /** Whether sandboxes can be snapshotted, and whether saving `pauses` or `stops` (Vercel) them. */
22
+ snapshots: "pauses" | "stops" | "none";
23
+ };
24
+ export type SandboxCommandResult = {
25
+ exitCode: number;
26
+ stdout: string;
27
+ stderr: string;
28
+ };
29
+ export type SandboxPreviewAccess = {
30
+ url: string;
31
+ headers: Record<string, string>;
32
+ };
33
+ export type SandboxFailureCode = "auth" | "not_found" | "rate_limited" | "timeout" | "provider";
34
+ /** A provider or API failure, as opposed to a command that exited non-zero. */
35
+ export type SandboxProviderFailure = Error & {
36
+ name: "SandboxProviderError";
37
+ code: SandboxFailureCode;
38
+ };
39
+ export declare function isSandboxProviderError(error: unknown): error is SandboxProviderFailure;
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { SandboxProviderKey } from "./sandbox/types.js";
1
2
  export type AuthStatus = {
2
3
  loggedIn: true;
3
4
  userId: string;
@@ -103,7 +104,9 @@ export type WorkspaceListResult = {
103
104
  hasMore: boolean;
104
105
  };
105
106
  };
106
- export type ProviderKey = "railway" | "aws" | "e2b" | "daytona" | "cloudflare" | "vercel" | "ascii" | "exedev";
107
+ export type ProviderKey = "railway" | "aws" | "e2b" | "daytona" | "cloudflare" | "vercel" | "boat" | "exedev"
108
+ /** gitterm.dev's own sandboxes: pick a size (`small` or `medium`) as the machine. */
109
+ | "gitterm";
107
110
  type ProviderSelectionBase = {
108
111
  /** Select a specific provider installation. Usually omitted. */
109
112
  providerId?: string;
@@ -122,7 +125,7 @@ export type WorkspaceProviderSelection = {
122
125
  region?: string;
123
126
  accessProfile?: string;
124
127
  } & ProviderSelectionBase) | ({
125
- type: "daytona" | "vercel" | "exedev" | "e2b" | "ascii";
128
+ type: "daytona" | "vercel" | "exedev" | "e2b" | "boat" | "gitterm";
126
129
  } & ProviderSelectionBase) | {
127
130
  type: "cloudflare";
128
131
  providerId?: string;
@@ -777,4 +780,50 @@ export type SavedBot = {
777
780
  /** When the settings last changed (ISO 8601); absent from older servers. */
778
781
  updatedAt?: string;
779
782
  };
783
+ /** A plain command sandbox billed to the signed-in Gitterm account. No agent runs inside it. */
784
+ export type ManagedSandbox = {
785
+ id: string;
786
+ provider: SandboxProviderKey;
787
+ name: string | null;
788
+ status: WorkspaceStatus;
789
+ metadata: Record<string, string>;
790
+ createdAt: string;
791
+ pausedAt: string | null;
792
+ terminatedAt: string | null;
793
+ lastActiveAt: string | null;
794
+ /** When Gitterm terminates the sandbox regardless of activity. */
795
+ expiresAt: string | null;
796
+ /** Terminated and confirmed deleted at the provider. A terminated sandbox without this is still being cleaned up. */
797
+ deleted: boolean;
798
+ machineProfileId: string | null;
799
+ };
800
+ /** A job finished in one of your sandboxes (Gitterm sandboxes on gitterm.dev only). */
801
+ export type SandboxJobFinished = {
802
+ sandboxId: string;
803
+ jobId: string;
804
+ status: string;
805
+ exitCode: number | null;
806
+ };
807
+ export type ManagedSandboxCreateInput = {
808
+ /** Defaults to e2b. Must be enabled for sandboxes on the deployment. */
809
+ provider?: SandboxProviderKey;
810
+ name?: string;
811
+ lifetimeMinutes: number;
812
+ /** Machine profile key, e.g. `small` or `large`. Defaults to the plan's default size. */
813
+ machine?: string;
814
+ /** Serve preview URLs without the access header. Cannot be changed later. */
815
+ publicPreviews?: boolean;
816
+ metadata?: Record<string, string>;
817
+ idempotencyKey?: string;
818
+ /** Start from one of your snapshots (same provider). It keeps the size it was saved at. */
819
+ snapshotId?: string;
820
+ };
821
+ /** A managed sandbox saved as a snapshot, counted against your plan's snapshot limit. */
822
+ export type ManagedSandboxSnapshot = {
823
+ id: string;
824
+ provider: SandboxProviderKey;
825
+ label: string;
826
+ machineProfileId: string | null;
827
+ createdAt: string;
828
+ };
780
829
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitterm/sdk",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -19,13 +19,25 @@
19
19
  "bun": "./dist/direct/index.js",
20
20
  "import": "./dist/direct/index.js",
21
21
  "default": "./dist/direct/index.js"
22
+ },
23
+ "./sandbox": {
24
+ "types": "./dist/sandbox/index.d.ts",
25
+ "bun": "./dist/sandbox/index.js",
26
+ "import": "./dist/sandbox/index.js",
27
+ "default": "./dist/sandbox/index.js"
28
+ },
29
+ "./sandbox/store": {
30
+ "types": "./dist/sandbox/store.d.ts",
31
+ "bun": "./dist/sandbox/store.js",
32
+ "import": "./dist/sandbox/store.js",
33
+ "default": "./dist/sandbox/store.js"
22
34
  }
23
35
  },
24
36
  "publishConfig": {
25
37
  "access": "public"
26
38
  },
27
39
  "scripts": {
28
- "build": "rm -rf dist && bun build src/index.ts src/direct/index.ts --outdir dist --target node --format esm --external @trpc/client --external eventsource --external e2b --external @asciidev/box-sdk --external @daytonaio/sdk --external @vercel/sandbox && tsc -p tsconfig.build.json",
40
+ "build": "rm -rf dist && bun build src/index.ts src/direct/index.ts src/sandbox/index.ts src/sandbox/store.ts --outdir dist --target node --format esm --external @trpc/client --external eventsource --external e2b --external @asciidev/box-sdk --external @daytonaio/sdk --external @vercel/sandbox && tsc -p tsconfig.build.json",
29
41
  "prepublishOnly": "bun run check-types && bun run build",
30
42
  "check-types": "tsc --noEmit"
31
43
  },
@@ -40,7 +52,7 @@
40
52
  "devDependencies": {
41
53
  "@gitterm/agent-runtime": "workspace:*",
42
54
  "@gitterm/api": "workspace:*",
43
- "@opencode/client": "^2.0.19",
55
+ "@opencode/client": "2.2.6",
44
56
  "@trpc/server": "catalog:",
45
57
  "@types/bun": "^1.2.6",
46
58
  "typescript": "^5.8.2"
@@ -1,2 +0,0 @@
1
- import type { AsciiDirectProviderConfig, DirectProviderAdapter } from "./types.js";
2
- export declare function createAsciiDirectProvider(config: AsciiDirectProviderConfig): DirectProviderAdapter;