@tagma/sdk 0.5.2 → 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 +573 -573
- package/dist/drivers/opencode.d.ts.map +1 -1
- package/dist/drivers/opencode.js +47 -17
- package/dist/drivers/opencode.js.map +1 -1
- package/package.json +2 -2
- package/scripts/preinstall.js +31 -31
- package/src/adapters/stdin-approval.ts +106 -106
- package/src/adapters/websocket-approval.ts +224 -224
- package/src/approval.ts +131 -131
- package/src/bootstrap.ts +37 -37
- package/src/completions/exit-code.ts +34 -34
- package/src/completions/file-exists.ts +66 -66
- package/src/completions/output-check.ts +92 -92
- package/src/config-ops.ts +307 -307
- package/src/drivers/opencode.ts +68 -29
- package/src/engine.ts +1220 -1220
- package/src/hooks.ts +193 -193
- package/src/logger.ts +182 -182
- package/src/middlewares/static-context.ts +49 -49
- package/src/pipeline-runner.ts +173 -173
- package/src/registry.ts +267 -267
- package/src/runner.ts +460 -460
- package/src/schema.test.ts +101 -101
- package/src/schema.ts +338 -338
- package/src/sdk.ts +118 -118
- package/src/triggers/file.ts +164 -164
- package/src/triggers/manual.ts +86 -86
- package/src/types.ts +18 -18
- package/src/utils.ts +203 -203
- package/src/validate-raw.ts +412 -412
- package/dist/drivers/claude-code.d.ts +0 -3
- package/dist/drivers/claude-code.d.ts.map +0 -1
- package/dist/drivers/claude-code.js +0 -225
- package/dist/drivers/claude-code.js.map +0 -1
- package/dist/templates.d.ts +0 -20
- package/dist/templates.d.ts.map +0 -1
- package/dist/templates.js +0 -93
- package/dist/templates.js.map +0 -1
package/src/drivers/opencode.ts
CHANGED
|
@@ -33,17 +33,25 @@ const EFFORT_TO_VARIANT: Record<string, string | null> = {
|
|
|
33
33
|
|
|
34
34
|
// ── Auto-install + free-model picker ───────────────────────────────────────
|
|
35
35
|
//
|
|
36
|
-
// The opencode driver is SDK-built-in, but the `opencode` CLI isn't
|
|
37
|
-
//
|
|
38
|
-
// sensible default model from whatever the CLI reports. Both checks are
|
|
39
|
-
// process-cached via module-level variables so each concern runs at most
|
|
40
|
-
// once per SDK process.
|
|
36
|
+
// The opencode driver is SDK-built-in, but the `opencode` CLI isn't. Two
|
|
37
|
+
// provisioning paths:
|
|
41
38
|
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
39
|
+
// 1. Desktop app — the Electron shell ships a platform-matched opencode
|
|
40
|
+
// binary under resources/opencode/bin/, prepended to the sidecar's PATH
|
|
41
|
+
// at launch (see packages/electron/src/runtime-paths.ts). In-app updates
|
|
42
|
+
// drop a newer copy into userData/opencode/bin/ which wins via PATH
|
|
43
|
+
// precedence. That path resolves on the first `opencode --version`
|
|
44
|
+
// probe below; no auto-install ever fires.
|
|
45
|
+
//
|
|
46
|
+
// 2. SDK direct use — when bun is on PATH we fall through to
|
|
47
|
+
// `bun install -g opencode-ai`, identical to the pre-desktop behavior.
|
|
48
|
+
//
|
|
49
|
+
// When BOTH paths are unavailable (no bundled binary, no bun) we fail with
|
|
50
|
+
// an actionable error pointing at the desktop Settings panel instead of
|
|
51
|
+
// silently letting `opencode run` ENOENT later — the old behavior swallowed
|
|
52
|
+
// the root cause in runCapture's catch and left the user staring at an
|
|
53
|
+
// opaque "exit code -1". The result is process-memoized so subsequent
|
|
54
|
+
// tasks in the same run surface the same error without re-probing.
|
|
47
55
|
|
|
48
56
|
interface OpencodeModelInfo {
|
|
49
57
|
id?: string;
|
|
@@ -53,7 +61,12 @@ interface OpencodeModelInfo {
|
|
|
53
61
|
limit?: { context?: number };
|
|
54
62
|
}
|
|
55
63
|
|
|
64
|
+
// Memoize BOTH success and failure. On failure we stash the message so every
|
|
65
|
+
// subsequent ensureOpencodeInstalled() throws the identical error — re-running
|
|
66
|
+
// the bun-install probe for each task of a failed run would just be slow and
|
|
67
|
+
// produce confusing interleaved stderr.
|
|
56
68
|
let opencodeReady: boolean | undefined;
|
|
69
|
+
let opencodeReadyError: string | undefined;
|
|
57
70
|
let cachedDefaultModel: string | undefined;
|
|
58
71
|
|
|
59
72
|
async function runCapture(
|
|
@@ -72,14 +85,38 @@ async function runCapture(
|
|
|
72
85
|
}
|
|
73
86
|
}
|
|
74
87
|
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
// Shared tail for every failure message — the Tagma desktop app exposes a
|
|
89
|
+
// one-click installer at the same npm source path this driver would reach
|
|
90
|
+
// for, so point users there first. Users running the SDK as a library still
|
|
91
|
+
// see the manual bun/npm hint.
|
|
92
|
+
const SETUP_HINT =
|
|
93
|
+
'If you are using the Tagma desktop app, open Editor Settings → OpenCode CLI to install or update the bundled binary. ' +
|
|
94
|
+
'Otherwise install it manually: `bun install -g opencode-ai` or `npm install -g opencode-ai`.';
|
|
95
|
+
|
|
96
|
+
async function ensureOpencodeInstalled(): Promise<void> {
|
|
97
|
+
if (opencodeReady === true) return;
|
|
98
|
+
if (opencodeReady === false && opencodeReadyError) {
|
|
99
|
+
throw new Error(opencodeReadyError);
|
|
100
|
+
}
|
|
77
101
|
|
|
78
|
-
// Probe existing install first —
|
|
102
|
+
// Probe existing install first — this is the hot path for desktop users
|
|
103
|
+
// (bundled binary in PATH) and for anyone who already has opencode.
|
|
79
104
|
const probe = await runCapture(['opencode', '--version']);
|
|
80
105
|
if (probe.code === 0) {
|
|
81
106
|
opencodeReady = true;
|
|
82
|
-
return
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Distinguish "bun is missing" from "bun is here but install failed" so
|
|
111
|
+
// the error we surface points at the right next step. If bun is absent we
|
|
112
|
+
// skip the install attempt entirely — spawning with `bun` as argv[0]
|
|
113
|
+
// would just ENOENT inside runCapture's catch and look identical to a
|
|
114
|
+
// failed install.
|
|
115
|
+
const bunProbe = await runCapture(['bun', '--version']);
|
|
116
|
+
if (bunProbe.code !== 0) {
|
|
117
|
+
opencodeReady = false;
|
|
118
|
+
opencodeReadyError = `OpenCode CLI is not available and \`bun\` is not installed. ${SETUP_HINT}`;
|
|
119
|
+
throw new Error(opencodeReadyError);
|
|
83
120
|
}
|
|
84
121
|
|
|
85
122
|
console.error(
|
|
@@ -93,9 +130,9 @@ async function ensureOpencodeInstalled(): Promise<boolean> {
|
|
|
93
130
|
});
|
|
94
131
|
const installCode = await install.exited;
|
|
95
132
|
if (installCode !== 0) {
|
|
96
|
-
console.error('[driver:opencode] install failed — opencode run will likely fail below.');
|
|
97
133
|
opencodeReady = false;
|
|
98
|
-
|
|
134
|
+
opencodeReadyError = `\`bun install -g opencode-ai\` failed (exit code ${installCode}). ${SETUP_HINT}`;
|
|
135
|
+
throw new Error(opencodeReadyError);
|
|
99
136
|
}
|
|
100
137
|
|
|
101
138
|
// Bun installs globals under `~/.bun/bin` (or `%USERPROFILE%\.bun\bin`),
|
|
@@ -113,13 +150,14 @@ async function ensureOpencodeInstalled(): Promise<boolean> {
|
|
|
113
150
|
}
|
|
114
151
|
|
|
115
152
|
const verify = await runCapture(['opencode', '--version']);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
'
|
|
120
|
-
|
|
153
|
+
if (verify.code !== 0) {
|
|
154
|
+
opencodeReady = false;
|
|
155
|
+
opencodeReadyError =
|
|
156
|
+
'`opencode` is not resolvable after `bun install -g opencode-ai` completed. ' +
|
|
157
|
+
"Bun's global bin directory is probably not on PATH — add it manually or restart the app.";
|
|
158
|
+
throw new Error(opencodeReadyError);
|
|
121
159
|
}
|
|
122
|
-
|
|
160
|
+
opencodeReady = true;
|
|
123
161
|
}
|
|
124
162
|
|
|
125
163
|
// `opencode models --verbose` emits "<provider>/<id>\n{...json...}\n" pairs.
|
|
@@ -174,11 +212,11 @@ function pickFreeModel(models: OpencodeModelInfo[]): string | null {
|
|
|
174
212
|
|
|
175
213
|
async function resolveDefaultModel(): Promise<string> {
|
|
176
214
|
if (cachedDefaultModel !== undefined) return cachedDefaultModel;
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
215
|
+
// ensureOpencodeInstalled now throws with an actionable message when the
|
|
216
|
+
// CLI can't be provisioned, so we let the error bubble up to the task
|
|
217
|
+
// runner instead of silently falling back to DEFAULT_MODEL (which would
|
|
218
|
+
// produce a second confusing ENOENT a few lines later in `opencode run`).
|
|
219
|
+
await ensureOpencodeInstalled();
|
|
182
220
|
console.error('[driver:opencode] resolving free opencode model...');
|
|
183
221
|
const { code, stdout } = await runCapture(['opencode', 'models', '--verbose']);
|
|
184
222
|
if (code !== 0) {
|
|
@@ -207,8 +245,9 @@ export const OpenCodeDriver: DriverPlugin = {
|
|
|
207
245
|
async buildCommand(task: TaskConfig, track: TrackConfig, ctx: DriverContext): Promise<SpawnSpec> {
|
|
208
246
|
const explicitModel = task.model ?? track.model;
|
|
209
247
|
// Always make sure the opencode CLI is usable before we spawn it — even
|
|
210
|
-
// when the user pinned a model.
|
|
211
|
-
//
|
|
248
|
+
// when the user pinned a model. ensureOpencodeInstalled throws with an
|
|
249
|
+
// actionable message when the binary is neither present on PATH (desktop
|
|
250
|
+
// bundles it there via runtime-paths.ts) nor installable via bun.
|
|
212
251
|
if (explicitModel) await ensureOpencodeInstalled();
|
|
213
252
|
// Otherwise resolveDefaultModel both ensures the CLI and picks a free
|
|
214
253
|
// model from `opencode models --verbose` (cached per-process).
|