@ai-sdk/harness-opencode 1.0.52 → 1.0.54
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 +18 -0
- package/dist/bridge/index.mjs +125 -78
- package/dist/bridge/index.mjs.map +1 -1
- package/dist/index.js +9 -6
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/bridge/create-emit-stream-event.ts +114 -49
- package/src/bridge/index.ts +19 -21
- package/src/bridge/opencode-events.ts +62 -37
- package/src/bridge/opencode-finish-step.ts +14 -3
- package/src/bridge/opencode-types.ts +33 -0
- package/src/bridge/opencode-usage.ts +21 -12
- package/src/opencode-harness.ts +9 -5
package/src/bridge/index.ts
CHANGED
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
type BridgeEvent,
|
|
4
4
|
type BridgeTurn,
|
|
5
5
|
} from '@ai-sdk/harness/bridge';
|
|
6
|
-
import { isRecord } from '@ai-sdk/provider-utils';
|
|
7
6
|
import { randomUUID } from 'node:crypto';
|
|
8
7
|
import { mkdirSync } from 'node:fs';
|
|
9
8
|
import path from 'node:path';
|
|
@@ -19,7 +18,6 @@ import {
|
|
|
19
18
|
emitOpenCodeStreamStart,
|
|
20
19
|
getOpenCodeEventSessionId,
|
|
21
20
|
isStepSettlementEvent,
|
|
22
|
-
type OpenCodeEvent,
|
|
23
21
|
type TranslationState,
|
|
24
22
|
unwrapOpenCodeEvent,
|
|
25
23
|
} from './opencode-events';
|
|
@@ -35,6 +33,11 @@ import {
|
|
|
35
33
|
type HarnessUsage,
|
|
36
34
|
type OpenCodeTokenUsage,
|
|
37
35
|
} from './opencode-usage';
|
|
36
|
+
import {
|
|
37
|
+
asOpenCodeObject,
|
|
38
|
+
type OpenCodeEvent,
|
|
39
|
+
type OpenCodeObject,
|
|
40
|
+
} from './opencode-types';
|
|
38
41
|
import { startAuthorizedToolRelay, type ToolRelay } from './tool-relay';
|
|
39
42
|
|
|
40
43
|
type Emit = (msg: Record<string, unknown>) => void;
|
|
@@ -834,12 +837,7 @@ async function handlePermissionV2({
|
|
|
834
837
|
? props.resources.map(String)
|
|
835
838
|
: [],
|
|
836
839
|
requestID,
|
|
837
|
-
toolCallId:
|
|
838
|
-
typeof props.source === 'object' &&
|
|
839
|
-
props.source !== null &&
|
|
840
|
-
'callID' in props.source
|
|
841
|
-
? String((props.source as { callID?: unknown }).callID)
|
|
842
|
-
: requestID,
|
|
840
|
+
toolCallId: String(props.source?.callID ?? requestID),
|
|
843
841
|
permissionMode,
|
|
844
842
|
builtinToolFiltering,
|
|
845
843
|
turn,
|
|
@@ -873,16 +871,12 @@ async function handlePermission({
|
|
|
873
871
|
const props = event.properties ?? {};
|
|
874
872
|
const requestID = String(props.id ?? '');
|
|
875
873
|
if (!requestID) return;
|
|
874
|
+
const tool = asOpenCodeObject(props.tool);
|
|
876
875
|
const reply = await selectPermissionReply({
|
|
877
876
|
action: String(props.permission ?? ''),
|
|
878
877
|
resources: Array.isArray(props.patterns) ? props.patterns.map(String) : [],
|
|
879
878
|
requestID,
|
|
880
|
-
toolCallId:
|
|
881
|
-
typeof props.tool === 'object' &&
|
|
882
|
-
props.tool !== null &&
|
|
883
|
-
'callID' in props.tool
|
|
884
|
-
? String((props.tool as { callID?: unknown }).callID)
|
|
885
|
-
: requestID,
|
|
879
|
+
toolCallId: String(tool?.callID ?? requestID),
|
|
886
880
|
permissionMode,
|
|
887
881
|
builtinToolFiltering,
|
|
888
882
|
turn,
|
|
@@ -1302,15 +1296,13 @@ function modelRefFromAssistantSnapshot(
|
|
|
1302
1296
|
const direct = modelRefFromValue(assistant);
|
|
1303
1297
|
if (direct) return direct;
|
|
1304
1298
|
|
|
1305
|
-
|
|
1306
|
-
return modelRefFromValue(assistant.metadata.assistant);
|
|
1307
|
-
}
|
|
1308
|
-
return undefined;
|
|
1299
|
+
return modelRefFromValue(asOpenCodeObject(assistant.metadata)?.assistant);
|
|
1309
1300
|
}
|
|
1310
1301
|
|
|
1311
1302
|
function modelRefFromSessionInfo(data: unknown): OpenCodeModelRef | undefined {
|
|
1312
|
-
|
|
1313
|
-
|
|
1303
|
+
const session = asOpenCodeObject(data);
|
|
1304
|
+
if (!session) return undefined;
|
|
1305
|
+
return modelRefFromValue(session.model) ?? modelRefFromObject(session);
|
|
1314
1306
|
}
|
|
1315
1307
|
|
|
1316
1308
|
function modelRefFromStart(start: StartMessage): OpenCodeModelRef | undefined {
|
|
@@ -1324,7 +1316,13 @@ function modelRefFromStart(start: StartMessage): OpenCodeModelRef | undefined {
|
|
|
1324
1316
|
}
|
|
1325
1317
|
|
|
1326
1318
|
function modelRefFromValue(value: unknown): OpenCodeModelRef | undefined {
|
|
1327
|
-
|
|
1319
|
+
const model = asOpenCodeObject(value);
|
|
1320
|
+
return model ? modelRefFromObject(model) : undefined;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
function modelRefFromObject(
|
|
1324
|
+
value: OpenCodeObject,
|
|
1325
|
+
): OpenCodeModelRef | undefined {
|
|
1328
1326
|
const providerID = stringValue(value.providerID);
|
|
1329
1327
|
const modelID = stringValue(value.modelID ?? value.id);
|
|
1330
1328
|
if (!providerID || !modelID) return undefined;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
type
|
|
6
|
-
|
|
7
|
-
};
|
|
1
|
+
import {
|
|
2
|
+
asOpenCodeObject,
|
|
3
|
+
type OpenCodeEvent,
|
|
4
|
+
type OpenCodeEventProperties,
|
|
5
|
+
type OpenCodeMessageInfo,
|
|
6
|
+
} from './opencode-types';
|
|
8
7
|
|
|
9
8
|
type Emit = (msg: Record<string, unknown>) => void;
|
|
10
9
|
|
|
@@ -53,10 +52,12 @@ export function emitOpenCodeStreamStart({
|
|
|
53
52
|
state: TranslationState;
|
|
54
53
|
emit: Emit;
|
|
55
54
|
}): void {
|
|
56
|
-
if (state.streamStarted
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
if (state.streamStarted) return;
|
|
56
|
+
const message = openCodeMessageInfoFromValue(info);
|
|
57
|
+
if (!message) return;
|
|
58
|
+
if (message.role !== 'assistant' && message.type !== 'assistant') return;
|
|
59
|
+
const providerID = stringValue(message.providerID);
|
|
60
|
+
const modelID = stringValue(message.modelID);
|
|
60
61
|
const modelId =
|
|
61
62
|
providerID && modelID ? `${providerID}/${modelID}` : undefined;
|
|
62
63
|
|
|
@@ -67,20 +68,24 @@ export function emitOpenCodeStreamStart({
|
|
|
67
68
|
export function unwrapOpenCodeEvent(
|
|
68
69
|
rawEvent: unknown,
|
|
69
70
|
): OpenCodeEvent | undefined {
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
const raw = asOpenCodeObject(rawEvent);
|
|
72
|
+
if (!raw) return undefined;
|
|
72
73
|
if (raw.type === 'sync' && raw.syncEvent) {
|
|
73
|
-
const sync = raw.syncEvent
|
|
74
|
+
const sync = asOpenCodeObject(raw.syncEvent);
|
|
75
|
+
if (!sync) return undefined;
|
|
74
76
|
return {
|
|
75
77
|
id: String(sync.id ?? raw.id ?? ''),
|
|
76
78
|
type: stripSyncVersion(String(sync.type ?? '')),
|
|
77
|
-
properties:
|
|
79
|
+
properties: openCodeEventPropertiesFromValue(sync.data) ?? {},
|
|
78
80
|
};
|
|
79
81
|
}
|
|
80
82
|
return {
|
|
81
83
|
id: typeof raw.id === 'string' ? raw.id : undefined,
|
|
82
84
|
type: typeof raw.type === 'string' ? stripSyncVersion(raw.type) : undefined,
|
|
83
|
-
properties:
|
|
85
|
+
properties:
|
|
86
|
+
openCodeEventPropertiesFromValue(raw.properties) ??
|
|
87
|
+
openCodeEventPropertiesFromValue(raw.data) ??
|
|
88
|
+
{},
|
|
84
89
|
};
|
|
85
90
|
}
|
|
86
91
|
|
|
@@ -95,14 +100,8 @@ export function getOpenCodeEventSessionId(
|
|
|
95
100
|
return props.id;
|
|
96
101
|
}
|
|
97
102
|
const part = props.part;
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
typeof part === 'object' &&
|
|
101
|
-
!Array.isArray(part) &&
|
|
102
|
-
typeof (part as { sessionID?: unknown }).sessionID === 'string'
|
|
103
|
-
) {
|
|
104
|
-
return (part as { sessionID: string }).sessionID;
|
|
105
|
-
}
|
|
103
|
+
const partObject = asOpenCodeObject(part);
|
|
104
|
+
if (typeof partObject?.sessionID === 'string') return partObject.sessionID;
|
|
106
105
|
return undefined;
|
|
107
106
|
}
|
|
108
107
|
|
|
@@ -202,21 +201,21 @@ export function emitLegacyTextPartUpdate({
|
|
|
202
201
|
state: TranslationState;
|
|
203
202
|
emit: Emit;
|
|
204
203
|
}): boolean {
|
|
205
|
-
|
|
206
|
-
if (
|
|
207
|
-
const id = stringValue(
|
|
204
|
+
const textPart = legacyTextPartFromValue(part);
|
|
205
|
+
if (!textPart) return false;
|
|
206
|
+
const id = stringValue(textPart.id);
|
|
208
207
|
if (!id) return true;
|
|
209
208
|
|
|
210
|
-
const messageID = stringValue(
|
|
209
|
+
const messageID = stringValue(textPart.messageID);
|
|
211
210
|
if (messageID && state.messageRoles.get(messageID) === 'user') return true;
|
|
212
211
|
|
|
213
|
-
const isReasoning =
|
|
212
|
+
const isReasoning = textPart.type === 'reasoning';
|
|
214
213
|
const ids = isReasoning
|
|
215
214
|
? state.legacyReasoningPartIds
|
|
216
215
|
: state.legacyTextPartIds;
|
|
217
216
|
const deltaMap = isReasoning ? state.reasoningDeltas : state.textDeltas;
|
|
218
217
|
const deltaType = isReasoning ? 'reasoning-delta' : 'text-delta';
|
|
219
|
-
const text = typeof
|
|
218
|
+
const text = typeof textPart.text === 'string' ? textPart.text : undefined;
|
|
220
219
|
|
|
221
220
|
startLegacyPart({
|
|
222
221
|
ids,
|
|
@@ -236,7 +235,7 @@ export function emitLegacyTextPartUpdate({
|
|
|
236
235
|
deltaMap.set(id, text);
|
|
237
236
|
}
|
|
238
237
|
|
|
239
|
-
if (
|
|
238
|
+
if (textPart.time?.end != null) {
|
|
240
239
|
ids.delete(id);
|
|
241
240
|
deltaMap.delete(id);
|
|
242
241
|
emit({ type: isReasoning ? 'reasoning-end' : 'text-end', id });
|
|
@@ -249,10 +248,6 @@ function stripSyncVersion(type: string): string {
|
|
|
249
248
|
return type.replace(/\.\d+$/, '');
|
|
250
249
|
}
|
|
251
250
|
|
|
252
|
-
function asRecord(value: unknown): Record<string, any> | undefined {
|
|
253
|
-
return isRecord(value) ? value : undefined;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
251
|
function stringValue(value: unknown): string | undefined {
|
|
257
252
|
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
258
253
|
}
|
|
@@ -283,6 +278,36 @@ function startLegacyPart({
|
|
|
283
278
|
emit({ type: `${type}-start`, id });
|
|
284
279
|
}
|
|
285
280
|
|
|
286
|
-
function
|
|
287
|
-
|
|
281
|
+
export function openCodeMessageInfoFromValue(
|
|
282
|
+
value: unknown,
|
|
283
|
+
): OpenCodeMessageInfo | undefined {
|
|
284
|
+
return asOpenCodeObject(value) as OpenCodeMessageInfo | undefined;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function openCodeEventPropertiesFromValue(
|
|
288
|
+
value: unknown,
|
|
289
|
+
): OpenCodeEventProperties | undefined {
|
|
290
|
+
return asOpenCodeObject(value) as OpenCodeEventProperties | undefined;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
type LegacyTextPart = {
|
|
294
|
+
type: 'text' | 'reasoning';
|
|
295
|
+
id?: unknown;
|
|
296
|
+
messageID?: unknown;
|
|
297
|
+
text?: unknown;
|
|
298
|
+
time?: { end?: unknown };
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
function legacyTextPartFromValue(value: unknown): LegacyTextPart | undefined {
|
|
302
|
+
const part = asOpenCodeObject(value);
|
|
303
|
+
if (!part || (part.type !== 'text' && part.type !== 'reasoning')) {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
type: part.type,
|
|
308
|
+
id: part.id,
|
|
309
|
+
messageID: part.messageID,
|
|
310
|
+
text: part.text,
|
|
311
|
+
time: asOpenCodeObject(part.time),
|
|
312
|
+
};
|
|
288
313
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { HarnessV1StreamPart } from '@ai-sdk/harness';
|
|
2
|
-
import { isRecord } from '@ai-sdk/provider-utils';
|
|
3
2
|
import { mapUsage } from './opencode-usage';
|
|
3
|
+
import { asOpenCodeObject } from './opencode-types';
|
|
4
4
|
|
|
5
5
|
type FinishStepEvent = Extract<HarnessV1StreamPart, { type: 'finish-step' }>;
|
|
6
6
|
|
|
@@ -20,9 +20,16 @@ export function mapOpenCodeFinishReason(
|
|
|
20
20
|
export function legacyStepFinishPartToFinishStep(
|
|
21
21
|
part: unknown,
|
|
22
22
|
): FinishStepEvent | undefined {
|
|
23
|
-
|
|
23
|
+
return translateLegacyStepFinishPart(part)?.event;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function translateLegacyStepFinishPart(
|
|
27
|
+
value: unknown,
|
|
28
|
+
): { event: FinishStepEvent; partId?: string } | undefined {
|
|
29
|
+
const part = asOpenCodeObject(value);
|
|
30
|
+
if (!part || part.type !== 'step-finish') return undefined;
|
|
24
31
|
const rawFinish = typeof part.reason === 'string' ? part.reason : 'stop';
|
|
25
|
-
|
|
32
|
+
const event: FinishStepEvent = {
|
|
26
33
|
type: 'finish-step',
|
|
27
34
|
finishReason: {
|
|
28
35
|
unified: mapOpenCodeFinishReason(rawFinish),
|
|
@@ -33,4 +40,8 @@ export function legacyStepFinishPartToFinishStep(
|
|
|
33
40
|
? { harnessMetadata: { opencode: { cost: part.cost } } }
|
|
34
41
|
: {}),
|
|
35
42
|
};
|
|
43
|
+
return {
|
|
44
|
+
event,
|
|
45
|
+
...(typeof part.id === 'string' ? { partId: part.id } : {}),
|
|
46
|
+
};
|
|
36
47
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type OpenCodeObject = Record<string, unknown>;
|
|
2
|
+
|
|
3
|
+
export type OpenCodeMessageInfo = {
|
|
4
|
+
id?: unknown;
|
|
5
|
+
role?: unknown;
|
|
6
|
+
type?: unknown;
|
|
7
|
+
providerID?: unknown;
|
|
8
|
+
modelID?: unknown;
|
|
9
|
+
tokens?: unknown;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type OpenCodeEventProperties = OpenCodeObject & {
|
|
13
|
+
info?: OpenCodeMessageInfo;
|
|
14
|
+
provider?: { metadata?: unknown };
|
|
15
|
+
source?: { callID?: unknown };
|
|
16
|
+
status?: {
|
|
17
|
+
type?: unknown;
|
|
18
|
+
attempt?: unknown;
|
|
19
|
+
message?: unknown;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type OpenCodeEvent = {
|
|
24
|
+
id?: string;
|
|
25
|
+
type?: string;
|
|
26
|
+
properties?: OpenCodeEventProperties;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export function asOpenCodeObject(value: unknown): OpenCodeObject | undefined {
|
|
30
|
+
return value != null && typeof value === 'object' && !Array.isArray(value)
|
|
31
|
+
? (value as OpenCodeObject)
|
|
32
|
+
: undefined;
|
|
33
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HarnessV1StreamPart } from '@ai-sdk/harness';
|
|
2
|
-
import {
|
|
2
|
+
import { asOpenCodeObject } from './opencode-types';
|
|
3
3
|
|
|
4
4
|
type FinishStepEvent = Extract<HarnessV1StreamPart, { type: 'finish-step' }>;
|
|
5
5
|
|
|
@@ -40,13 +40,13 @@ export function defaultUsage(): HarnessUsage {
|
|
|
40
40
|
export function extractSessionTokens(
|
|
41
41
|
value: unknown,
|
|
42
42
|
): OpenCodeTokenUsage | undefined {
|
|
43
|
-
const record =
|
|
43
|
+
const record = asOpenCodeObject(value) as SessionTokenEnvelope | undefined;
|
|
44
44
|
if (!record) return undefined;
|
|
45
45
|
const tokens =
|
|
46
46
|
extractOpenCodeTokens(record.tokens) ??
|
|
47
|
-
extractOpenCodeTokens(
|
|
48
|
-
extractOpenCodeTokens(
|
|
49
|
-
extractOpenCodeTokens(
|
|
47
|
+
extractOpenCodeTokens(record.info?.tokens) ??
|
|
48
|
+
extractOpenCodeTokens(record.data?.tokens) ??
|
|
49
|
+
extractOpenCodeTokens(record.data?.data?.tokens);
|
|
50
50
|
return tokens;
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -105,8 +105,8 @@ export function addUsage({
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
function extractOpenCodeTokens(value: unknown): OpenCodeTokenUsage | undefined {
|
|
108
|
-
const record =
|
|
109
|
-
const cache =
|
|
108
|
+
const record = asOpenCodeObject(value);
|
|
109
|
+
const cache = asOpenCodeObject(record?.cache);
|
|
110
110
|
if (!record || !cache) return undefined;
|
|
111
111
|
return {
|
|
112
112
|
input: numberValue(record.input),
|
|
@@ -129,11 +129,11 @@ function zeroOpenCodeTokens(): OpenCodeTokenUsage {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
function asTokenGroup(value: unknown): Record<string, number | undefined> {
|
|
132
|
-
return
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
132
|
+
return (
|
|
133
|
+
(asOpenCodeObject(value) as
|
|
134
|
+
| Record<string, number | undefined>
|
|
135
|
+
| undefined) ?? {}
|
|
136
|
+
);
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
function numberValue(value: unknown): number {
|
|
@@ -157,3 +157,12 @@ function add({
|
|
|
157
157
|
? undefined
|
|
158
158
|
: (leftNumber ?? 0) + (rightNumber ?? 0);
|
|
159
159
|
}
|
|
160
|
+
|
|
161
|
+
type SessionTokenEnvelope = {
|
|
162
|
+
tokens?: unknown;
|
|
163
|
+
info?: { tokens?: unknown };
|
|
164
|
+
data?: {
|
|
165
|
+
tokens?: unknown;
|
|
166
|
+
data?: { tokens?: unknown };
|
|
167
|
+
};
|
|
168
|
+
};
|
package/src/opencode-harness.ts
CHANGED
|
@@ -182,7 +182,7 @@ const OPENCODE_BUILTIN_TOOLS = {
|
|
|
182
182
|
}),
|
|
183
183
|
} as const satisfies Record<string, HarnessV1BuiltinTool<any, any>>;
|
|
184
184
|
|
|
185
|
-
const BOOTSTRAP_DIR = '
|
|
185
|
+
const BOOTSTRAP_DIR = '.harness-bootstrap/opencode';
|
|
186
186
|
|
|
187
187
|
const openCodeBridgeCoordsSchema = z.object({
|
|
188
188
|
port: z.number(),
|
|
@@ -230,12 +230,12 @@ export function createOpenCode(
|
|
|
230
230
|
},
|
|
231
231
|
],
|
|
232
232
|
commands: [
|
|
233
|
-
{ command: `mkdir -p ${BOOTSTRAP_DIR}` },
|
|
234
233
|
{
|
|
235
|
-
command:
|
|
234
|
+
command: 'pnpm install --frozen-lockfile --store-dir .pnpm-store',
|
|
236
235
|
},
|
|
237
236
|
{
|
|
238
|
-
command:
|
|
237
|
+
command:
|
|
238
|
+
'node node_modules/opencode-ai/postinstall.mjs && ./node_modules/.bin/opencode --version',
|
|
239
239
|
},
|
|
240
240
|
],
|
|
241
241
|
};
|
|
@@ -245,6 +245,10 @@ export function createOpenCode(
|
|
|
245
245
|
const sandboxSession = startOpts.sandboxSession;
|
|
246
246
|
const session = sandboxSession.restricted();
|
|
247
247
|
const sandboxId = sandboxSession.id;
|
|
248
|
+
const bootstrapDir = path.posix.resolve(
|
|
249
|
+
sandboxSession.defaultWorkingDirectory,
|
|
250
|
+
BOOTSTRAP_DIR,
|
|
251
|
+
);
|
|
248
252
|
const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;
|
|
249
253
|
const isResume = lifecycleState != null;
|
|
250
254
|
const isContinue = startOpts.continueFrom != null;
|
|
@@ -388,7 +392,7 @@ export function createOpenCode(
|
|
|
388
392
|
});
|
|
389
393
|
|
|
390
394
|
const proc = await session.spawn({
|
|
391
|
-
command: `node ${
|
|
395
|
+
command: `node ${shellQuote(`${bootstrapDir}/bridge.mjs`)} --workdir ${shellQuote(workDir)} --bridge-state-dir ${shellQuote(bridgeStateDir)} --bootstrap-dir ${shellQuote(bootstrapDir)}${skillSetup ? ` --skills-dir ${shellQuote(skillSetup.skillsDir)}` : ''}`,
|
|
392
396
|
env,
|
|
393
397
|
abortSignal: startOpts.abortSignal,
|
|
394
398
|
});
|