@ai-sdk/harness-opencode 1.0.88 → 1.0.92
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/CHANGELOG.md +34 -0
- package/dist/bridge/index.mjs +61 -3
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.js +95 -39
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/bridge/create-emit-stream-event.ts +21 -0
- package/src/bridge/index.ts +61 -3
- package/src/bridge/opencode-events.ts +10 -0
- package/src/bridge/opencode-types.ts +1 -0
- package/src/opencode-auth.ts +73 -26
- package/src/opencode-harness.ts +67 -34
package/src/opencode-auth.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
HarnessV1RequestTransformation,
|
|
3
|
+
HarnessV1RequestTransformationSources,
|
|
4
|
+
} from '@ai-sdk/harness';
|
|
2
5
|
import {
|
|
3
6
|
createCredentialRequestTransformation,
|
|
4
7
|
getAiGatewayAuthFromEnv,
|
|
@@ -11,47 +14,91 @@ export const OPENCODE_CREDENTIAL_ENVIRONMENT_VARIABLES = [
|
|
|
11
14
|
'ANTHROPIC_AUTH_TOKEN',
|
|
12
15
|
] as const;
|
|
13
16
|
|
|
14
|
-
export function createOpenCodeRequestTransformations(
|
|
15
|
-
env:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
export function createOpenCodeRequestTransformations({
|
|
18
|
+
env: environment,
|
|
19
|
+
sandboxEnv: sandboxEnvironment,
|
|
20
|
+
auth: authenticationMode,
|
|
21
|
+
}: HarnessV1RequestTransformationSources<OpenCodeResolvedAuthenticationMode>): HarnessV1RequestTransformation[] {
|
|
22
|
+
switch (authenticationMode) {
|
|
19
23
|
case 'ai-gateway':
|
|
20
|
-
return
|
|
24
|
+
return environment.AI_GATEWAY_API_KEY &&
|
|
25
|
+
sandboxEnvironment.AI_GATEWAY_API_KEY
|
|
21
26
|
? [
|
|
22
27
|
createCredentialRequestTransformation({
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
matchUrl: environment.AI_GATEWAY_BASE_URL,
|
|
29
|
+
matchHeaders: {
|
|
30
|
+
'x-api-key': sandboxEnvironment.AI_GATEWAY_API_KEY,
|
|
31
|
+
},
|
|
32
|
+
transformHeaders: {
|
|
33
|
+
Authorization: `Bearer ${environment.AI_GATEWAY_API_KEY}`,
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
createCredentialRequestTransformation({
|
|
37
|
+
matchUrl: environment.AI_GATEWAY_BASE_URL,
|
|
38
|
+
matchHeaders: {
|
|
39
|
+
Authorization: `Bearer ${sandboxEnvironment.AI_GATEWAY_API_KEY}`,
|
|
40
|
+
},
|
|
41
|
+
transformHeaders: {
|
|
42
|
+
Authorization: `Bearer ${environment.AI_GATEWAY_API_KEY}`,
|
|
26
43
|
},
|
|
27
44
|
}),
|
|
28
45
|
]
|
|
29
46
|
: [];
|
|
30
47
|
case 'openai':
|
|
31
|
-
return
|
|
48
|
+
return environment.OPENAI_API_KEY && sandboxEnvironment.OPENAI_API_KEY
|
|
32
49
|
? [
|
|
33
50
|
createCredentialRequestTransformation({
|
|
34
|
-
|
|
35
|
-
|
|
51
|
+
matchUrl:
|
|
52
|
+
environment.OPENAI_BASE_URL ?? 'https://api.openai.com/v1',
|
|
53
|
+
matchHeaders: {
|
|
54
|
+
Authorization: `Bearer ${sandboxEnvironment.OPENAI_API_KEY}`,
|
|
55
|
+
},
|
|
56
|
+
transformHeaders: {
|
|
57
|
+
Authorization: `Bearer ${environment.OPENAI_API_KEY}`,
|
|
58
|
+
},
|
|
36
59
|
}),
|
|
37
60
|
]
|
|
38
61
|
: [];
|
|
39
62
|
case 'anthropic': {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
63
|
+
const matchUrl =
|
|
64
|
+
environment.ANTHROPIC_BASE_URL ?? 'https://api.anthropic.com';
|
|
65
|
+
const transformations: HarnessV1RequestTransformation[] = [];
|
|
66
|
+
|
|
67
|
+
if (
|
|
68
|
+
environment.ANTHROPIC_API_KEY != null &&
|
|
69
|
+
sandboxEnvironment.ANTHROPIC_API_KEY != null
|
|
70
|
+
) {
|
|
71
|
+
transformations.push(
|
|
72
|
+
createCredentialRequestTransformation({
|
|
73
|
+
matchUrl,
|
|
74
|
+
matchHeaders: {
|
|
75
|
+
'x-api-key': sandboxEnvironment.ANTHROPIC_API_KEY,
|
|
76
|
+
},
|
|
77
|
+
transformHeaders: {
|
|
78
|
+
'x-api-key': environment.ANTHROPIC_API_KEY,
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
43
82
|
}
|
|
44
|
-
|
|
45
|
-
|
|
83
|
+
|
|
84
|
+
if (
|
|
85
|
+
environment.ANTHROPIC_AUTH_TOKEN != null &&
|
|
86
|
+
sandboxEnvironment.ANTHROPIC_AUTH_TOKEN != null
|
|
87
|
+
) {
|
|
88
|
+
transformations.push(
|
|
89
|
+
createCredentialRequestTransformation({
|
|
90
|
+
matchUrl,
|
|
91
|
+
matchHeaders: {
|
|
92
|
+
Authorization: `Bearer ${sandboxEnvironment.ANTHROPIC_AUTH_TOKEN}`,
|
|
93
|
+
},
|
|
94
|
+
transformHeaders: {
|
|
95
|
+
Authorization: `Bearer ${environment.ANTHROPIC_AUTH_TOKEN}`,
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
);
|
|
46
99
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
: [
|
|
50
|
-
createCredentialRequestTransformation({
|
|
51
|
-
baseUrl: env.ANTHROPIC_BASE_URL ?? 'https://api.anthropic.com',
|
|
52
|
-
headers,
|
|
53
|
-
}),
|
|
54
|
-
];
|
|
100
|
+
|
|
101
|
+
return transformations;
|
|
55
102
|
}
|
|
56
103
|
}
|
|
57
104
|
}
|
package/src/opencode-harness.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
import {
|
|
24
24
|
applyCredentialForwarding,
|
|
25
25
|
classifyDiskLog,
|
|
26
|
+
createSandboxCredentialEnvironment,
|
|
26
27
|
experimental_createBridgeUserMessageSubmitter,
|
|
27
28
|
createBridgeErrorHandler,
|
|
28
29
|
createBridgeStartupError,
|
|
@@ -30,7 +31,6 @@ import {
|
|
|
30
31
|
forwardBridgeProcessStream,
|
|
31
32
|
getRestrictedSandboxSession,
|
|
32
33
|
markBridgeStarting,
|
|
33
|
-
maskSandboxCredentials,
|
|
34
34
|
resolveSandboxDefaultWorkingDirectory,
|
|
35
35
|
resolveSandboxHomeDir,
|
|
36
36
|
SandboxChannel,
|
|
@@ -226,6 +226,7 @@ const openCodeBridgeCoordsSchema = z.object({
|
|
|
226
226
|
const openCodeResumeStateSchema = z.object({
|
|
227
227
|
openCodeSessionId: z.string().optional(),
|
|
228
228
|
bridge: openCodeBridgeCoordsSchema.optional(),
|
|
229
|
+
sandboxCredentialEnvironment: z.record(z.string(), z.string()).optional(),
|
|
229
230
|
});
|
|
230
231
|
|
|
231
232
|
type OpenCodeBridgeCoords = z.infer<typeof openCodeBridgeCoordsSchema>;
|
|
@@ -270,6 +271,23 @@ export function createOpenCode(
|
|
|
270
271
|
sandboxSession,
|
|
271
272
|
abortSignal: startOpts.abortSignal,
|
|
272
273
|
});
|
|
274
|
+
const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;
|
|
275
|
+
const isResume = lifecycleState != null;
|
|
276
|
+
const isContinue = startOpts.continueFrom != null;
|
|
277
|
+
const resumeData =
|
|
278
|
+
isResume && typeof lifecycleState?.data === 'object'
|
|
279
|
+
? (lifecycleState.data as {
|
|
280
|
+
openCodeSessionId?: unknown;
|
|
281
|
+
bridge?: OpenCodeBridgeCoords;
|
|
282
|
+
sandboxCredentialEnvironment?: Record<string, string>;
|
|
283
|
+
})
|
|
284
|
+
: undefined;
|
|
285
|
+
const resumeSessionId =
|
|
286
|
+
typeof resumeData?.openCodeSessionId === 'string' &&
|
|
287
|
+
resumeData.openCodeSessionId.length > 0
|
|
288
|
+
? resumeData.openCodeSessionId
|
|
289
|
+
: undefined;
|
|
290
|
+
const coords = resumeData?.bridge;
|
|
273
291
|
const authenticationMode = resolveOpenCodeAuthenticationMode({
|
|
274
292
|
auth: settings.auth,
|
|
275
293
|
model: settings.model,
|
|
@@ -281,24 +299,35 @@ export function createOpenCode(
|
|
|
281
299
|
provider: settings.provider,
|
|
282
300
|
});
|
|
283
301
|
let sandboxAuthEnvironment = resolvedAuthEnvironment;
|
|
302
|
+
let sandboxCredentialEnvironment: Record<string, string> | undefined;
|
|
303
|
+
let credentialsBrokered = false;
|
|
284
304
|
if (
|
|
285
305
|
'addRequestTransformations' in sandboxSession &&
|
|
286
306
|
sandboxSession.addRequestTransformations != null
|
|
287
307
|
) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
308
|
+
sandboxCredentialEnvironment =
|
|
309
|
+
resumeData?.sandboxCredentialEnvironment ??
|
|
310
|
+
(await createSandboxCredentialEnvironment({
|
|
311
|
+
environment: resolvedAuthEnvironment,
|
|
312
|
+
credentialEnvironmentVariables:
|
|
313
|
+
OPENCODE_CREDENTIAL_ENVIRONMENT_VARIABLES,
|
|
314
|
+
credentialForwarding: settings.credentialForwarding,
|
|
315
|
+
}));
|
|
316
|
+
sandboxAuthEnvironment = {
|
|
317
|
+
...resolvedAuthEnvironment,
|
|
318
|
+
...sandboxCredentialEnvironment,
|
|
319
|
+
};
|
|
320
|
+
const requestTransformations = createOpenCodeRequestTransformations({
|
|
321
|
+
env: resolvedAuthEnvironment,
|
|
322
|
+
sandboxEnv: sandboxAuthEnvironment,
|
|
323
|
+
auth: authenticationMode,
|
|
324
|
+
});
|
|
292
325
|
if (requestTransformations.length > 0) {
|
|
293
326
|
await sandboxSession.addRequestTransformations(
|
|
294
327
|
requestTransformations,
|
|
295
328
|
);
|
|
296
329
|
}
|
|
297
|
-
|
|
298
|
-
environment: resolvedAuthEnvironment,
|
|
299
|
-
credentialEnvironmentVariables:
|
|
300
|
-
OPENCODE_CREDENTIAL_ENVIRONMENT_VARIABLES,
|
|
301
|
-
});
|
|
330
|
+
credentialsBrokered = true;
|
|
302
331
|
} else {
|
|
303
332
|
warnCredentialBrokeringUnavailable();
|
|
304
333
|
}
|
|
@@ -306,23 +335,6 @@ export function createOpenCode(
|
|
|
306
335
|
defaultWorkingDirectory,
|
|
307
336
|
BOOTSTRAP_DIR,
|
|
308
337
|
);
|
|
309
|
-
const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;
|
|
310
|
-
const isResume = lifecycleState != null;
|
|
311
|
-
const isContinue = startOpts.continueFrom != null;
|
|
312
|
-
const resumeData =
|
|
313
|
-
isResume && typeof lifecycleState?.data === 'object'
|
|
314
|
-
? (lifecycleState.data as {
|
|
315
|
-
openCodeSessionId?: unknown;
|
|
316
|
-
bridge?: OpenCodeBridgeCoords;
|
|
317
|
-
})
|
|
318
|
-
: undefined;
|
|
319
|
-
const resumeSessionId =
|
|
320
|
-
typeof resumeData?.openCodeSessionId === 'string' &&
|
|
321
|
-
resumeData.openCodeSessionId.length > 0
|
|
322
|
-
? resumeData.openCodeSessionId
|
|
323
|
-
: undefined;
|
|
324
|
-
const coords = resumeData?.bridge;
|
|
325
|
-
|
|
326
338
|
const workDir = startOpts.sessionWorkDir;
|
|
327
339
|
const sessionDataDir = `${defaultWorkingDirectory}/.agent-runs/${startOpts.sessionId}`;
|
|
328
340
|
const bridgeStateDir = `${sessionDataDir}/bridge`;
|
|
@@ -386,6 +398,7 @@ export function createOpenCode(
|
|
|
386
398
|
bridgePort: coords.port,
|
|
387
399
|
bridgeToken: coords.token,
|
|
388
400
|
sandboxId,
|
|
401
|
+
sandboxCredentialEnvironment,
|
|
389
402
|
debug: startOpts.observability?.debug,
|
|
390
403
|
permissionMode: startOpts.permissionMode,
|
|
391
404
|
builtinToolFiltering: startOpts.builtinToolFiltering,
|
|
@@ -434,12 +447,14 @@ export function createOpenCode(
|
|
|
434
447
|
abortSignal: startOpts.abortSignal,
|
|
435
448
|
})
|
|
436
449
|
: undefined;
|
|
437
|
-
const forwardedAuthEnvironment =
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
450
|
+
const forwardedAuthEnvironment = credentialsBrokered
|
|
451
|
+
? sandboxAuthEnvironment
|
|
452
|
+
: await applyCredentialForwarding({
|
|
453
|
+
environment: sandboxAuthEnvironment,
|
|
454
|
+
credentialEnvironmentVariables:
|
|
455
|
+
OPENCODE_CREDENTIAL_ENVIRONMENT_VARIABLES,
|
|
456
|
+
credentialForwarding: settings.credentialForwarding,
|
|
457
|
+
});
|
|
443
458
|
const env = {
|
|
444
459
|
...forwardedAuthEnvironment,
|
|
445
460
|
AI_SDK_HARNESS_CLIENT_APP: OPENCODE_CLIENT_APP,
|
|
@@ -552,6 +567,7 @@ export function createOpenCode(
|
|
|
552
567
|
bridgePort: boundPort,
|
|
553
568
|
bridgeToken: token,
|
|
554
569
|
sandboxId,
|
|
570
|
+
sandboxCredentialEnvironment,
|
|
555
571
|
debug: startOpts.observability?.debug,
|
|
556
572
|
permissionMode: startOpts.permissionMode,
|
|
557
573
|
builtinToolFiltering: startOpts.builtinToolFiltering,
|
|
@@ -779,6 +795,7 @@ function createSession({
|
|
|
779
795
|
bridgePort,
|
|
780
796
|
bridgeToken,
|
|
781
797
|
sandboxId,
|
|
798
|
+
sandboxCredentialEnvironment,
|
|
782
799
|
debug,
|
|
783
800
|
permissionMode,
|
|
784
801
|
builtinToolFiltering,
|
|
@@ -798,6 +815,7 @@ function createSession({
|
|
|
798
815
|
bridgePort: number;
|
|
799
816
|
bridgeToken: string;
|
|
800
817
|
sandboxId: string | undefined;
|
|
818
|
+
sandboxCredentialEnvironment: Record<string, string> | undefined;
|
|
801
819
|
debug: HarnessV1DebugConfig | undefined;
|
|
802
820
|
permissionMode: HarnessV1PermissionMode | undefined;
|
|
803
821
|
builtinToolFiltering: HarnessV1BuiltinToolFiltering | undefined;
|
|
@@ -1092,6 +1110,9 @@ function createSession({
|
|
|
1092
1110
|
...(latestOpenCodeSessionId
|
|
1093
1111
|
? { openCodeSessionId: latestOpenCodeSessionId }
|
|
1094
1112
|
: {}),
|
|
1113
|
+
...(sandboxCredentialEnvironment == null
|
|
1114
|
+
? {}
|
|
1115
|
+
: { sandboxCredentialEnvironment }),
|
|
1095
1116
|
bridge: {
|
|
1096
1117
|
port: bridgePort,
|
|
1097
1118
|
token: bridgeToken,
|
|
@@ -1185,11 +1206,20 @@ function createSession({
|
|
|
1185
1206
|
channel.close();
|
|
1186
1207
|
}
|
|
1187
1208
|
|
|
1209
|
+
const lifecycleData =
|
|
1210
|
+
data != null && typeof data === 'object' && !Array.isArray(data)
|
|
1211
|
+
? { ...(data as Record<string, unknown>) }
|
|
1212
|
+
: {};
|
|
1188
1213
|
const payload: HarnessV1ResumeSessionState = {
|
|
1189
1214
|
type: 'resume-session',
|
|
1190
1215
|
harnessId: 'opencode',
|
|
1191
1216
|
specificationVersion: 'harness-v1',
|
|
1192
|
-
data:
|
|
1217
|
+
data: {
|
|
1218
|
+
...lifecycleData,
|
|
1219
|
+
...(sandboxCredentialEnvironment == null
|
|
1220
|
+
? {}
|
|
1221
|
+
: { sandboxCredentialEnvironment }),
|
|
1222
|
+
} as HarnessV1ResumeSessionState['data'],
|
|
1193
1223
|
};
|
|
1194
1224
|
return payload;
|
|
1195
1225
|
},
|
|
@@ -1209,6 +1239,9 @@ function createSession({
|
|
|
1209
1239
|
...(latestOpenCodeSessionId
|
|
1210
1240
|
? { openCodeSessionId: latestOpenCodeSessionId }
|
|
1211
1241
|
: {}),
|
|
1242
|
+
...(sandboxCredentialEnvironment == null
|
|
1243
|
+
? {}
|
|
1244
|
+
: { sandboxCredentialEnvironment }),
|
|
1212
1245
|
bridge: {
|
|
1213
1246
|
port: bridgePort,
|
|
1214
1247
|
token: bridgeToken,
|