@skelm/opencode 0.4.2 → 0.4.4
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 +2 -2
- package/dist/backend.d.ts +9 -17
- package/dist/backend.js +27 -50
- package/dist/client.js +55 -15
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/provider.js +4 -4
- package/dist/types.d.ts +8 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ export default defineConfig({
|
|
|
40
40
|
A workflow that applies a fix to a codebase:
|
|
41
41
|
|
|
42
42
|
```ts
|
|
43
|
-
// workflows/fix-bug.workflow.
|
|
43
|
+
// workflows/fix-bug.workflow.mts
|
|
44
44
|
import { agent, pipeline } from 'skelm'
|
|
45
45
|
import { z } from 'zod'
|
|
46
46
|
|
|
@@ -74,7 +74,7 @@ export default pipeline({
|
|
|
74
74
|
## What's exported
|
|
75
75
|
|
|
76
76
|
```ts
|
|
77
|
-
export { createOpencodeBackend
|
|
77
|
+
export { createOpencodeBackend } from './backend.js'
|
|
78
78
|
export { createOpencodeBackendFromConfig } from './factory.js'
|
|
79
79
|
export { OpencodeProvider, createOpencodeProvider } from './provider.js'
|
|
80
80
|
export { OpencodeClientWrapper } from './client.js'
|
package/dist/backend.d.ts
CHANGED
|
@@ -15,25 +15,17 @@ export declare function createOpencodeBackend(options: OpencodeBackendOptions):
|
|
|
15
15
|
* Custom error types for opencode backend
|
|
16
16
|
*/
|
|
17
17
|
export declare class BackendAuthenticationError extends Error {
|
|
18
|
-
constructor(message: string
|
|
18
|
+
constructor(message: string, options?: {
|
|
19
|
+
cause?: unknown;
|
|
20
|
+
});
|
|
19
21
|
}
|
|
20
22
|
export declare class BackendRateLimitError extends Error {
|
|
21
|
-
constructor(message: string
|
|
23
|
+
constructor(message: string, options?: {
|
|
24
|
+
cause?: unknown;
|
|
25
|
+
});
|
|
22
26
|
}
|
|
23
27
|
export declare class BackendTimeoutError extends Error {
|
|
24
|
-
constructor(message: string
|
|
28
|
+
constructor(message: string, options?: {
|
|
29
|
+
cause?: unknown;
|
|
30
|
+
});
|
|
25
31
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Create an opencode backend with ACP compatibility mode
|
|
28
|
-
*
|
|
29
|
-
* This runs opencode as a subprocess via ACP instead of using the SDK directly.
|
|
30
|
-
* Useful for testing or when API access is restricted.
|
|
31
|
-
*/
|
|
32
|
-
export declare function createOpencodeAcpBackend(options: {
|
|
33
|
-
command?: string;
|
|
34
|
-
args?: readonly string[];
|
|
35
|
-
cwd?: string;
|
|
36
|
-
env?: NodeJS.ProcessEnv;
|
|
37
|
-
id?: string;
|
|
38
|
-
label?: string;
|
|
39
|
-
}): SkelmBackend;
|
package/dist/backend.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { loadSkillBodies } from '@skelm/core';
|
|
1
|
+
import { PermissionDeniedError, loadSkillBodies } from '@skelm/core';
|
|
2
2
|
import { OpencodeClientWrapper } from './client.js';
|
|
3
|
-
import {
|
|
3
|
+
import { mapSkelmPermissionsToOpencode, validatePermissions } from './permission-mapper.js';
|
|
4
4
|
/**
|
|
5
5
|
* SkelmBackend implementation for opencode.ai with full permission enforcement
|
|
6
6
|
*
|
|
@@ -20,6 +20,13 @@ export function createOpencodeBackend(options) {
|
|
|
20
20
|
skills: true,
|
|
21
21
|
modelSelection: options.model !== undefined,
|
|
22
22
|
toolPermissions: 'native',
|
|
23
|
+
// Image content is threaded into opencode as a `FilePartInput` alongside
|
|
24
|
+
// the text part (see buildOpencodePromptParts in client.ts); whether the
|
|
25
|
+
// *upstream* model actually processes images depends on the configured
|
|
26
|
+
// opencode model (Sonnet, GPT-4o, etc. — see opencode docs). Set
|
|
27
|
+
// `vision: false` via the second capability block above if a deployment
|
|
28
|
+
// pins a known text-only model.
|
|
29
|
+
vision: options.vision ?? true,
|
|
23
30
|
};
|
|
24
31
|
// Single client instance per backend — server started on first call and
|
|
25
32
|
// kept alive for subsequent calls (one session per prompt call).
|
|
@@ -40,14 +47,9 @@ export function createOpencodeBackend(options) {
|
|
|
40
47
|
}),
|
|
41
48
|
});
|
|
42
49
|
if (!permissionResult.allowed) {
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
policy, permissionResult);
|
|
47
|
-
// In production, this would be written to the audit log
|
|
48
|
-
// For now, we log to console
|
|
49
|
-
console.warn('Permission denied:', JSON.stringify(auditEntry, null, 2));
|
|
50
|
-
throw new Error(`Permission denied: ${permissionResult.denied.join(', ')}`);
|
|
50
|
+
// Throw the typed error so the runner's audit writer (the single
|
|
51
|
+
// durable record) captures the denial; no parallel console log.
|
|
52
|
+
throw new PermissionDeniedError(`opencode: permission denied: ${permissionResult.denied.join(', ')}`);
|
|
51
53
|
}
|
|
52
54
|
// Load skills and inject into system prompt before forwarding
|
|
53
55
|
const enrichedRequest = await injectSkills(request, context);
|
|
@@ -59,14 +61,20 @@ export function createOpencodeBackend(options) {
|
|
|
59
61
|
}
|
|
60
62
|
catch (error) {
|
|
61
63
|
if (error instanceof Error) {
|
|
64
|
+
// The opencode SDK surfaces these conditions as plain Errors
|
|
65
|
+
// with descriptive messages; substring matching is the only
|
|
66
|
+
// signal available. The original error is attached as `cause`
|
|
67
|
+
// so callers retain stack and any provider-specific fields.
|
|
62
68
|
if (error.message.includes('Authentication')) {
|
|
63
|
-
throw new BackendAuthenticationError(`Opencode authentication failed: ${error.message}
|
|
69
|
+
throw new BackendAuthenticationError(`Opencode authentication failed: ${error.message}`, { cause: error });
|
|
64
70
|
}
|
|
65
71
|
if (error.message.includes('Rate limit')) {
|
|
66
|
-
throw new BackendRateLimitError(`Opencode rate limit exceeded: ${error.message}
|
|
72
|
+
throw new BackendRateLimitError(`Opencode rate limit exceeded: ${error.message}`, {
|
|
73
|
+
cause: error,
|
|
74
|
+
});
|
|
67
75
|
}
|
|
68
76
|
if (error.message.includes('timed out')) {
|
|
69
|
-
throw new BackendTimeoutError(error.message);
|
|
77
|
+
throw new BackendTimeoutError(error.message, { cause: error });
|
|
70
78
|
}
|
|
71
79
|
}
|
|
72
80
|
throw error;
|
|
@@ -123,51 +131,20 @@ function createEmptyPolicy() {
|
|
|
123
131
|
* Custom error types for opencode backend
|
|
124
132
|
*/
|
|
125
133
|
export class BackendAuthenticationError extends Error {
|
|
126
|
-
constructor(message) {
|
|
127
|
-
super(message);
|
|
134
|
+
constructor(message, options) {
|
|
135
|
+
super(message, options);
|
|
128
136
|
this.name = 'BackendAuthenticationError';
|
|
129
137
|
}
|
|
130
138
|
}
|
|
131
139
|
export class BackendRateLimitError extends Error {
|
|
132
|
-
constructor(message) {
|
|
133
|
-
super(message);
|
|
140
|
+
constructor(message, options) {
|
|
141
|
+
super(message, options);
|
|
134
142
|
this.name = 'BackendRateLimitError';
|
|
135
143
|
}
|
|
136
144
|
}
|
|
137
145
|
export class BackendTimeoutError extends Error {
|
|
138
|
-
constructor(message) {
|
|
139
|
-
super(message);
|
|
146
|
+
constructor(message, options) {
|
|
147
|
+
super(message, options);
|
|
140
148
|
this.name = 'BackendTimeoutError';
|
|
141
149
|
}
|
|
142
150
|
}
|
|
143
|
-
/**
|
|
144
|
-
* Create an opencode backend with ACP compatibility mode
|
|
145
|
-
*
|
|
146
|
-
* This runs opencode as a subprocess via ACP instead of using the SDK directly.
|
|
147
|
-
* Useful for testing or when API access is restricted.
|
|
148
|
-
*/
|
|
149
|
-
export function createOpencodeAcpBackend(options) {
|
|
150
|
-
const command = options.command ?? 'opencode';
|
|
151
|
-
const args = options.args ?? ['acp'];
|
|
152
|
-
const capabilities = {
|
|
153
|
-
prompt: true,
|
|
154
|
-
streaming: true,
|
|
155
|
-
sessionLifecycle: true,
|
|
156
|
-
mcp: true,
|
|
157
|
-
skills: false, // ACP mode has limited skill control
|
|
158
|
-
modelSelection: false,
|
|
159
|
-
toolPermissions: 'unsupported', // ACP forwards permissions as metadata only
|
|
160
|
-
};
|
|
161
|
-
const backend = {
|
|
162
|
-
id: options.id ?? 'opencode-acp',
|
|
163
|
-
capabilities,
|
|
164
|
-
...(options.label !== undefined && { label: options.label }),
|
|
165
|
-
async run(request, context) {
|
|
166
|
-
// ACP mode: permissions are advisory only
|
|
167
|
-
// Forward the request to opencode via stdio
|
|
168
|
-
// This is a placeholder - full ACP implementation would use the AcpClient
|
|
169
|
-
throw new Error('ACP mode not yet implemented. Use SDK mode with createOpencodeBackend() instead.');
|
|
170
|
-
},
|
|
171
|
-
};
|
|
172
|
-
return backend;
|
|
173
|
-
}
|
package/dist/client.js
CHANGED
|
@@ -8,7 +8,35 @@
|
|
|
8
8
|
// #3 — Non-blocking promptAsync + SSE stream instead of blocking session.prompt()
|
|
9
9
|
import { spawn } from 'node:child_process'; // @subprocess-ok: spawns opencode serve for HTTP backend
|
|
10
10
|
import { createOpencodeClient } from '@opencode-ai/sdk';
|
|
11
|
-
import { TrustEnforcer } from '@skelm/core';
|
|
11
|
+
import { BackendSessionError, RunCancelledError, TrustEnforcer, extractPromptText, } from '@skelm/core';
|
|
12
|
+
/**
|
|
13
|
+
* Map a skelm `AgentRequest.prompt` (string or `ContentPart[]`) onto
|
|
14
|
+
* opencode's prompt-parts shape. Text parts become `{type:'text'}`; image
|
|
15
|
+
* parts are forwarded as `{type:'file'}` with a base64 data URL, matching
|
|
16
|
+
* the opencode SDK's documented `FilePartInput` schema. opencode's own
|
|
17
|
+
* vision-capable models (Sonnet, GPT-4o, etc.) consume these attachments
|
|
18
|
+
* directly; non-vision models surface their own provider error which
|
|
19
|
+
* propagates as a thrown step failure.
|
|
20
|
+
*/
|
|
21
|
+
function buildOpencodePromptParts(prompt) {
|
|
22
|
+
if (typeof prompt === 'string') {
|
|
23
|
+
return [{ type: 'text', text: prompt }];
|
|
24
|
+
}
|
|
25
|
+
const parts = [];
|
|
26
|
+
for (const part of prompt) {
|
|
27
|
+
if (part.type === 'text') {
|
|
28
|
+
parts.push({ type: 'text', text: part.text });
|
|
29
|
+
}
|
|
30
|
+
else if (part.type === 'image') {
|
|
31
|
+
parts.push({
|
|
32
|
+
type: 'file',
|
|
33
|
+
mime: part.mimeType,
|
|
34
|
+
url: `data:${part.mimeType};base64,${part.data}`,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return parts.length > 0 ? parts : [{ type: 'text', text: extractPromptText(prompt) }];
|
|
39
|
+
}
|
|
12
40
|
// Module-singleton process-exit hook. Without this, every OpencodeClientWrapper
|
|
13
41
|
// that called process.once('SIGTERM', …) would add a fresh listener, tripping
|
|
14
42
|
// Node's MaxListeners=10 warning under any non-trivial backend churn. Wrappers
|
|
@@ -104,20 +132,32 @@ export class OpencodeClientWrapper {
|
|
|
104
132
|
liveChildren.add(proc);
|
|
105
133
|
ensureSignalHook();
|
|
106
134
|
let resolved = false;
|
|
107
|
-
let
|
|
108
|
-
const
|
|
135
|
+
let stdoutBuf = '';
|
|
136
|
+
const MAX_BUF_BYTES = 64 * 1024;
|
|
137
|
+
// opencode prints its listen URL on stdout. Parse stdout only and
|
|
138
|
+
// anchor strictly to a loopback URL so noisy stderr (or LLM output
|
|
139
|
+
// proxied through stderr) cannot redirect the gateway to an
|
|
140
|
+
// attacker-controlled URL. The buffer is capped so a flood of
|
|
141
|
+
// non-matching output can't be retained indefinitely.
|
|
142
|
+
const LISTEN_RE = /(?:^|\n)opencode(?: \w+)? listening on (https?:\/\/(?:127\.0\.0\.1|localhost|\[::1\]):\d+)(?=\s|$)/;
|
|
143
|
+
const tryParseStdout = (chunk) => {
|
|
109
144
|
if (resolved)
|
|
110
145
|
return;
|
|
111
|
-
|
|
112
|
-
|
|
146
|
+
stdoutBuf += chunk.toString('utf8');
|
|
147
|
+
if (stdoutBuf.length > MAX_BUF_BYTES) {
|
|
148
|
+
stdoutBuf = stdoutBuf.slice(stdoutBuf.length - MAX_BUF_BYTES);
|
|
149
|
+
}
|
|
150
|
+
const m = stdoutBuf.match(LISTEN_RE);
|
|
113
151
|
if (m?.[1]) {
|
|
114
152
|
resolved = true;
|
|
115
153
|
this.client = createOpencodeClient({ baseUrl: m[1].trim() });
|
|
116
154
|
resolve();
|
|
117
155
|
}
|
|
118
156
|
};
|
|
119
|
-
proc.stdout?.on('data',
|
|
120
|
-
|
|
157
|
+
proc.stdout?.on('data', tryParseStdout);
|
|
158
|
+
// stderr is consumed only to drain the pipe and avoid backpressure;
|
|
159
|
+
// its content is no longer parsed for the listen URL.
|
|
160
|
+
proc.stderr?.on('data', () => { });
|
|
121
161
|
proc.once('error', (err) => {
|
|
122
162
|
if (!resolved)
|
|
123
163
|
reject(err);
|
|
@@ -147,7 +187,7 @@ export class OpencodeClientWrapper {
|
|
|
147
187
|
async prompt(request, signal, timeoutMs = 300_000, resolvedPolicy, onPartial) {
|
|
148
188
|
await this.ensureStarted();
|
|
149
189
|
if (!this.client)
|
|
150
|
-
throw new
|
|
190
|
+
throw new BackendSessionError('opencode serve not ready', 'opencode');
|
|
151
191
|
const cwd = request.cwd ?? process.cwd();
|
|
152
192
|
// Forward each per-step McpServerConfig to opencode so its model sees the
|
|
153
193
|
// namespaced tools. The opencode subprocess persists across calls, so the
|
|
@@ -158,7 +198,7 @@ export class OpencodeClientWrapper {
|
|
|
158
198
|
}
|
|
159
199
|
const sessResult = await this.client.session.create({ query: { directory: cwd } });
|
|
160
200
|
if (!sessResult.data) {
|
|
161
|
-
throw new
|
|
201
|
+
throw new BackendSessionError(`session.create failed: ${JSON.stringify(sessResult.error)}`, 'opencode', { cause: sessResult.error });
|
|
162
202
|
}
|
|
163
203
|
const sessionId = sessResult.data.id;
|
|
164
204
|
// (#3) Subscribe to the global SSE stream BEFORE calling promptAsync so
|
|
@@ -174,7 +214,7 @@ export class OpencodeClientWrapper {
|
|
|
174
214
|
const promptResult = await this.client.session.promptAsync({
|
|
175
215
|
path: { id: sessionId },
|
|
176
216
|
body: {
|
|
177
|
-
parts:
|
|
217
|
+
parts: buildOpencodePromptParts(request.prompt),
|
|
178
218
|
...(request.system !== undefined && { system: request.system }),
|
|
179
219
|
...(resolvedPolicy !== undefined && {
|
|
180
220
|
tools: buildOpencodeToolsFromPolicy(resolvedPolicy),
|
|
@@ -182,7 +222,7 @@ export class OpencodeClientWrapper {
|
|
|
182
222
|
},
|
|
183
223
|
});
|
|
184
224
|
if (!promptResult.data) {
|
|
185
|
-
throw new
|
|
225
|
+
throw new BackendSessionError(`session.promptAsync failed: ${JSON.stringify(promptResult.error)}`, 'opencode', { cause: promptResult.error });
|
|
186
226
|
}
|
|
187
227
|
return await this._collectFromStream(stream, sessionId, signal, onPartial, resolvedPolicy);
|
|
188
228
|
}
|
|
@@ -209,7 +249,7 @@ export class OpencodeClientWrapper {
|
|
|
209
249
|
if (event.type === 'session.error') {
|
|
210
250
|
const props = event.properties;
|
|
211
251
|
if (!props.sessionID || props.sessionID === sessionId) {
|
|
212
|
-
throw new
|
|
252
|
+
throw new BackendSessionError(`opencode session error: ${JSON.stringify(props.error)}`, 'opencode', { cause: props.error });
|
|
213
253
|
}
|
|
214
254
|
}
|
|
215
255
|
// Opencode pauses the session and emits permission.asked when a tool
|
|
@@ -260,7 +300,7 @@ export class OpencodeClientWrapper {
|
|
|
260
300
|
}
|
|
261
301
|
}
|
|
262
302
|
if (signal.aborted)
|
|
263
|
-
throw new
|
|
303
|
+
throw new RunCancelledError();
|
|
264
304
|
const text = [...textParts.values()].join('');
|
|
265
305
|
return { text: text.trim(), stopReason: 'end_turn' };
|
|
266
306
|
}
|
|
@@ -276,7 +316,7 @@ export class OpencodeClientWrapper {
|
|
|
276
316
|
if (this.attachedMcp.has(server.id))
|
|
277
317
|
continue;
|
|
278
318
|
if (server.transport !== 'stdio') {
|
|
279
|
-
throw new
|
|
319
|
+
throw new BackendSessionError(`opencode backend currently only forwards stdio MCP servers; "${server.id}" uses ${server.transport}`, 'opencode');
|
|
280
320
|
}
|
|
281
321
|
const command = [server.command, ...(server.args ?? [])];
|
|
282
322
|
const result = await this.client.mcp.add({
|
|
@@ -291,7 +331,7 @@ export class OpencodeClientWrapper {
|
|
|
291
331
|
},
|
|
292
332
|
});
|
|
293
333
|
if (result.error !== undefined) {
|
|
294
|
-
throw new
|
|
334
|
+
throw new BackendSessionError(`opencode mcp.add(${server.id}) failed: ${JSON.stringify(result.error)}`, 'opencode', { cause: result.error });
|
|
295
335
|
}
|
|
296
336
|
this.attachedMcp.add(server.id);
|
|
297
337
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Full integration with opencode.ai coding agent via the official SDK,
|
|
5
5
|
* with granular permission enforcement and multi-agent support.
|
|
6
6
|
*/
|
|
7
|
-
export { createOpencodeBackend
|
|
7
|
+
export { createOpencodeBackend } from './backend.js';
|
|
8
8
|
export { createOpencodeBackendFromConfig } from './factory.js';
|
|
9
9
|
export type { OpencodeBackendConfig } from './factory.js';
|
|
10
10
|
export { OpencodeProvider, createOpencodeProvider } from './provider.js';
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Full integration with opencode.ai coding agent via the official SDK,
|
|
5
5
|
* with granular permission enforcement and multi-agent support.
|
|
6
6
|
*/
|
|
7
|
-
export { createOpencodeBackend
|
|
7
|
+
export { createOpencodeBackend } from './backend.js';
|
|
8
8
|
export { createOpencodeBackendFromConfig } from './factory.js';
|
|
9
9
|
export { OpencodeProvider, createOpencodeProvider } from './provider.js';
|
|
10
10
|
export { OpencodeClientWrapper } from './client.js';
|
package/dist/provider.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Implements ProviderPluginBase for the opencode.ai coding agent.
|
|
5
5
|
*/
|
|
6
|
-
import { ProviderPluginBase } from '@skelm/core';
|
|
6
|
+
import { BackendConfigError, ProviderPluginBase } from '@skelm/core';
|
|
7
7
|
import { createOpencodeBackend } from './backend.js';
|
|
8
8
|
/**
|
|
9
9
|
* Opencode provider implementation
|
|
@@ -83,7 +83,7 @@ export class OpencodeProvider extends ProviderPluginBase {
|
|
|
83
83
|
// Validate API key
|
|
84
84
|
const apiKey = config.apiKey ?? process.env.OPENCODE_API_KEY;
|
|
85
85
|
if (!apiKey) {
|
|
86
|
-
throw new
|
|
86
|
+
throw new BackendConfigError('Opencode API key is required. Set apiKey config or OPENCODE_API_KEY env var.', 'opencode');
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
@@ -91,7 +91,7 @@ export class OpencodeProvider extends ProviderPluginBase {
|
|
|
91
91
|
*/
|
|
92
92
|
async createBackend(options) {
|
|
93
93
|
if (!this.initialized) {
|
|
94
|
-
throw new
|
|
94
|
+
throw new BackendConfigError('Provider must be initialized before creating backends', 'opencode');
|
|
95
95
|
}
|
|
96
96
|
const config = {};
|
|
97
97
|
const apiKey = this.config.apiKey ?? process.env.OPENCODE_API_KEY;
|
|
@@ -199,7 +199,7 @@ export class OpencodeProvider extends ProviderPluginBase {
|
|
|
199
199
|
validateConfig(config) {
|
|
200
200
|
const apiKey = config.apiKey ?? process.env.OPENCODE_API_KEY;
|
|
201
201
|
if (!apiKey) {
|
|
202
|
-
throw new
|
|
202
|
+
throw new BackendConfigError('Opencode API key is required', 'opencode');
|
|
203
203
|
}
|
|
204
204
|
return config;
|
|
205
205
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -28,6 +28,14 @@ export interface OpencodeBackendOptions {
|
|
|
28
28
|
maxRetries?: number;
|
|
29
29
|
/** Log level */
|
|
30
30
|
logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'off';
|
|
31
|
+
/**
|
|
32
|
+
* Advertise `capabilities.vision`. Defaults to `true`: image content is
|
|
33
|
+
* forwarded to opencode as a `FilePartInput` and the upstream model
|
|
34
|
+
* decides whether it can process it. Set `false` when targeting an
|
|
35
|
+
* opencode model known to be text-only — the framework gate then rejects
|
|
36
|
+
* image prompts at step start with no opencode session ever spawned.
|
|
37
|
+
*/
|
|
38
|
+
vision?: boolean;
|
|
31
39
|
/** Egress proxy URL for outbound HTTP requests (e.g., 'http://127.0.0.1:14739'). */
|
|
32
40
|
egressProxyUrl?: string;
|
|
33
41
|
/** Egress token for proxy authentication (injected into subprocesses). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skelm/opencode",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Opencode.ai backend for skelm with full permission enforcement",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Scott Glover <scottgl@gmail.com>",
|
|
@@ -48,10 +48,10 @@
|
|
|
48
48
|
"@opencode-ai/sdk": "^1.14.33"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@skelm/core": "^0.4.
|
|
51
|
+
"@skelm/core": "^0.4.4"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@skelm/core": "^0.4.
|
|
54
|
+
"@skelm/core": "^0.4.4",
|
|
55
55
|
"@types/node": "^20.10.0",
|
|
56
56
|
"typescript": "^5.3.0",
|
|
57
57
|
"vitest": "^1.0.0"
|