@ai-sdk/harness-claude-code 1.0.167 → 1.0.168
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 +13 -0
- package/README.md +4 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -202
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/claude-code-harness.ts +32 -325
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/harness-claude-code",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.168",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@ai-sdk/harness": "1.0.
|
|
30
|
-
"@ai-sdk/provider": "4.0.
|
|
31
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
29
|
+
"@ai-sdk/harness": "1.0.164",
|
|
30
|
+
"@ai-sdk/provider": "4.0.34",
|
|
31
|
+
"@ai-sdk/provider-utils": "5.0.68",
|
|
32
32
|
"ws": "^8.21.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
type HarnessV1ResponseFormat,
|
|
17
17
|
type HarnessV1PortEndpoint,
|
|
18
18
|
type HarnessV1ResumeSessionState,
|
|
19
|
-
type HarnessV1NetworkSandboxSession,
|
|
20
19
|
type HarnessV1Session,
|
|
21
20
|
type HarnessV1Skill,
|
|
22
21
|
type HarnessV1StreamPart,
|
|
@@ -28,6 +27,7 @@ import {
|
|
|
28
27
|
classifyDiskLog,
|
|
29
28
|
resolveSandboxCredentialEnvironment,
|
|
30
29
|
createBridgeToken,
|
|
30
|
+
createBridgeWebSocketConnect,
|
|
31
31
|
experimental_createBridgeUserMessageSubmitter,
|
|
32
32
|
createBridgeErrorHandler,
|
|
33
33
|
createBridgeStartupError,
|
|
@@ -36,18 +36,19 @@ import {
|
|
|
36
36
|
forwardBridgeProcessStream,
|
|
37
37
|
getRestrictedSandboxSession,
|
|
38
38
|
markBridgeStarting,
|
|
39
|
+
resolveBridgeEndpoint,
|
|
40
|
+
resolveBridgePort,
|
|
39
41
|
resolveSandboxHomeDir,
|
|
40
42
|
SandboxChannel,
|
|
41
43
|
shellQuote,
|
|
42
|
-
|
|
44
|
+
validateBridgeSandboxSettings,
|
|
43
45
|
warnCredentialBrokeringUnavailable,
|
|
44
46
|
waitForBridgeReady,
|
|
45
|
-
withBridgeToken,
|
|
46
47
|
writeSkills,
|
|
48
|
+
type BridgeHello,
|
|
47
49
|
type SandboxChannelReconnectOptions,
|
|
48
50
|
} from '@ai-sdk/harness/utils';
|
|
49
51
|
import {
|
|
50
|
-
safeParseJSON,
|
|
51
52
|
tool,
|
|
52
53
|
type Experimental_SandboxSession as SandboxSession,
|
|
53
54
|
type Experimental_SandboxProcess,
|
|
@@ -862,18 +863,13 @@ export function createClaudeCode(
|
|
|
862
863
|
const toolSafeSandboxSession =
|
|
863
864
|
getRestrictedSandboxSession(sandboxSession);
|
|
864
865
|
const sandboxId = 'id' in sandboxSession ? sandboxSession.id : undefined;
|
|
865
|
-
|
|
866
|
+
validateBridgeSandboxSettings({
|
|
866
867
|
sandboxSession,
|
|
867
|
-
|
|
868
|
-
|
|
868
|
+
sandboxId,
|
|
869
|
+
portOverride: settings.port,
|
|
870
|
+
portEndpointOverride: settings.portEndpoint,
|
|
871
|
+
mintBridgeToken: settings.mintBridgeToken,
|
|
869
872
|
});
|
|
870
|
-
if (settings.mintBridgeToken != null && sandboxId == null) {
|
|
871
|
-
throw new HarnessCapabilityUnsupportedError({
|
|
872
|
-
harnessId: 'claude-code',
|
|
873
|
-
message:
|
|
874
|
-
'The Claude Code harness cannot use `mintBridgeToken` with a sandbox session that does not expose an id.',
|
|
875
|
-
});
|
|
876
|
-
}
|
|
877
873
|
const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;
|
|
878
874
|
const isResume = lifecycleState != null;
|
|
879
875
|
const isContinue = startOpts.continueFrom != null;
|
|
@@ -1000,26 +996,10 @@ export function createClaudeCode(
|
|
|
1000
996
|
sessionId: startOpts.sessionId,
|
|
1001
997
|
});
|
|
1002
998
|
let supportsUserMessageResponses = false;
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
const buildConnect =
|
|
1008
|
-
(endpoint: HarnessV1PortEndpoint) =>
|
|
1009
|
-
async ({
|
|
1010
|
-
abortSignal,
|
|
1011
|
-
}: {
|
|
1012
|
-
abortSignal: AbortSignal;
|
|
1013
|
-
}): Promise<WebSocket> => {
|
|
1014
|
-
return openBridgeWebSocket({
|
|
1015
|
-
endpoint,
|
|
1016
|
-
timeoutMs,
|
|
1017
|
-
abortSignal,
|
|
1018
|
-
onHello: supportsResponses => {
|
|
1019
|
-
supportsUserMessageResponses = supportsResponses;
|
|
1020
|
-
},
|
|
1021
|
-
});
|
|
1022
|
-
};
|
|
999
|
+
const onHello = (hello: BridgeHello) => {
|
|
1000
|
+
supportsUserMessageResponses =
|
|
1001
|
+
hello.capabilities?.experimental_userMessageResponses === true;
|
|
1002
|
+
};
|
|
1023
1003
|
|
|
1024
1004
|
/*
|
|
1025
1005
|
* Rung 1 — ATTACH. When lifecycle state carries live bridge coordinates,
|
|
@@ -1031,17 +1011,18 @@ export function createClaudeCode(
|
|
|
1031
1011
|
*/
|
|
1032
1012
|
if (coords) {
|
|
1033
1013
|
try {
|
|
1034
|
-
const
|
|
1014
|
+
const attachEndpoint = await resolveBridgeEndpoint({
|
|
1035
1015
|
sandboxSession,
|
|
1036
|
-
override: settings.portEndpoint,
|
|
1037
1016
|
port: coords.port,
|
|
1038
|
-
|
|
1039
|
-
const attachEndpoint = withBridgeToken({
|
|
1040
|
-
endpoint,
|
|
1017
|
+
portEndpointOverride: settings.portEndpoint,
|
|
1041
1018
|
token: coords.token,
|
|
1042
1019
|
});
|
|
1043
1020
|
const attachChannel: ClaudeCodeChannel = new SandboxChannel({
|
|
1044
|
-
connect:
|
|
1021
|
+
connect: createBridgeWebSocketConnect({
|
|
1022
|
+
endpoint: attachEndpoint,
|
|
1023
|
+
webSocketConstructor: WebSocket,
|
|
1024
|
+
onHello,
|
|
1025
|
+
}),
|
|
1045
1026
|
outboundSchema: outboundMessageSchema,
|
|
1046
1027
|
initialLastSeenEventId: coords.lastSeenEventId,
|
|
1047
1028
|
onDiagnostic,
|
|
@@ -1118,7 +1099,7 @@ export function createClaudeCode(
|
|
|
1118
1099
|
|
|
1119
1100
|
const port = resolveBridgePort({
|
|
1120
1101
|
sandboxSession,
|
|
1121
|
-
|
|
1102
|
+
portOverride: settings.port,
|
|
1122
1103
|
});
|
|
1123
1104
|
const token =
|
|
1124
1105
|
settings.mintBridgeToken == null
|
|
@@ -1191,15 +1172,21 @@ export function createClaudeCode(
|
|
|
1191
1172
|
});
|
|
1192
1173
|
void drainBridgeProcessStream(proc.stdout);
|
|
1193
1174
|
|
|
1194
|
-
const
|
|
1175
|
+
const bridgeEndpoint = await resolveBridgeEndpoint({
|
|
1195
1176
|
sandboxSession,
|
|
1196
|
-
override: settings.portEndpoint,
|
|
1197
1177
|
port: boundPort,
|
|
1178
|
+
portEndpointOverride: settings.portEndpoint,
|
|
1179
|
+
token,
|
|
1198
1180
|
});
|
|
1199
|
-
const bridgeEndpoint = withBridgeToken({ endpoint, token });
|
|
1200
1181
|
|
|
1201
1182
|
const channel: ClaudeCodeChannel = new SandboxChannel({
|
|
1202
|
-
connect:
|
|
1183
|
+
connect: createBridgeWebSocketConnect({
|
|
1184
|
+
endpoint: bridgeEndpoint,
|
|
1185
|
+
webSocketConstructor: WebSocket,
|
|
1186
|
+
openRetryTimeoutMs: timeoutMs,
|
|
1187
|
+
startAbortSignal: startOpts.abortSignal,
|
|
1188
|
+
onHello,
|
|
1189
|
+
}),
|
|
1203
1190
|
outboundSchema: outboundMessageSchema,
|
|
1204
1191
|
onDiagnostic,
|
|
1205
1192
|
onBridgeError,
|
|
@@ -1255,71 +1242,6 @@ export function createClaudeCode(
|
|
|
1255
1242
|
};
|
|
1256
1243
|
}
|
|
1257
1244
|
|
|
1258
|
-
function resolveBridgePort({
|
|
1259
|
-
sandboxSession,
|
|
1260
|
-
override,
|
|
1261
|
-
}: {
|
|
1262
|
-
sandboxSession: HarnessV1NetworkSandboxSession | SandboxSession;
|
|
1263
|
-
override: number | undefined;
|
|
1264
|
-
}): number {
|
|
1265
|
-
if (override !== undefined) return override;
|
|
1266
|
-
if ('ports' in sandboxSession && sandboxSession.ports.length > 0) {
|
|
1267
|
-
return sandboxSession.ports[0];
|
|
1268
|
-
}
|
|
1269
|
-
throw new HarnessCapabilityUnsupportedError({
|
|
1270
|
-
harnessId: 'claude-code',
|
|
1271
|
-
message:
|
|
1272
|
-
'The claude-code harness needs a TCP port exposed by the sandbox. ' +
|
|
1273
|
-
'Create the sandbox with `ports: [<port>]` or pass `createClaudeCode({ port })`.',
|
|
1274
|
-
});
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
function validateBasicSandboxSettings({
|
|
1278
|
-
sandboxSession,
|
|
1279
|
-
port,
|
|
1280
|
-
portEndpoint,
|
|
1281
|
-
}: {
|
|
1282
|
-
sandboxSession: HarnessV1NetworkSandboxSession | SandboxSession;
|
|
1283
|
-
port: number | undefined;
|
|
1284
|
-
portEndpoint: HarnessV1PortEndpoint | undefined;
|
|
1285
|
-
}): void {
|
|
1286
|
-
if ('getPortEndpoint' in sandboxSession) return;
|
|
1287
|
-
if (port == null) {
|
|
1288
|
-
throw new HarnessCapabilityUnsupportedError({
|
|
1289
|
-
harnessId: 'claude-code',
|
|
1290
|
-
message:
|
|
1291
|
-
'The Claude Code harness requires an explicit `port` when using a basic sandbox session.',
|
|
1292
|
-
});
|
|
1293
|
-
}
|
|
1294
|
-
if (portEndpoint == null) {
|
|
1295
|
-
throw new HarnessCapabilityUnsupportedError({
|
|
1296
|
-
harnessId: 'claude-code',
|
|
1297
|
-
message:
|
|
1298
|
-
'The Claude Code harness requires an explicit `portEndpoint` when using a basic sandbox session.',
|
|
1299
|
-
});
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
async function resolveBridgeEndpoint({
|
|
1304
|
-
sandboxSession,
|
|
1305
|
-
override,
|
|
1306
|
-
port,
|
|
1307
|
-
}: {
|
|
1308
|
-
sandboxSession: HarnessV1NetworkSandboxSession | SandboxSession;
|
|
1309
|
-
override: HarnessV1PortEndpoint | undefined;
|
|
1310
|
-
port: number;
|
|
1311
|
-
}): Promise<HarnessV1PortEndpoint> {
|
|
1312
|
-
if (override != null) return override;
|
|
1313
|
-
if ('getPortEndpoint' in sandboxSession) {
|
|
1314
|
-
return sandboxSession.getPortEndpoint({ port, protocol: 'ws' });
|
|
1315
|
-
}
|
|
1316
|
-
throw new HarnessCapabilityUnsupportedError({
|
|
1317
|
-
harnessId: 'claude-code',
|
|
1318
|
-
message:
|
|
1319
|
-
'The Claude Code harness requires an explicit `portEndpoint` when using a basic sandbox session.',
|
|
1320
|
-
});
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
1245
|
/**
|
|
1324
1246
|
* Materialise skill files into
|
|
1325
1247
|
* `$HOME/.claude/skills/<name>/SKILL.md`. The `claude` CLI
|
|
@@ -1327,221 +1249,6 @@ async function resolveBridgeEndpoint({
|
|
|
1327
1249
|
* be in place before the bridge is spawned without mutating the session
|
|
1328
1250
|
* workdir. Each file uses the YAML-frontmatter shape the CLI expects.
|
|
1329
1251
|
*/
|
|
1330
|
-
/**
|
|
1331
|
-
* Wait for the bridge's `bridge-hello` message to arrive on the freshly
|
|
1332
|
-
* opened WebSocket before any other host-side code touches it.
|
|
1333
|
-
*
|
|
1334
|
-
* Some sandbox runtimes (Vercel in particular) complete the WS upgrade
|
|
1335
|
-
* with the host long before the connection is actually forwarded to the
|
|
1336
|
-
* sandbox-side bridge. Anything sent in that gap is silently dropped —
|
|
1337
|
-
* including the `start` message we send next. The bridge emits
|
|
1338
|
-
* `bridge-hello` the instant it accepts the connection, so receiving it
|
|
1339
|
-
* is the only reliable evidence that the end-to-end link is live.
|
|
1340
|
-
*/
|
|
1341
|
-
function openWebSocketAndWaitForBridgeHello({
|
|
1342
|
-
endpoint,
|
|
1343
|
-
openTimeoutMs,
|
|
1344
|
-
getHelloTimeoutMs,
|
|
1345
|
-
abortSignal,
|
|
1346
|
-
onHello,
|
|
1347
|
-
}: {
|
|
1348
|
-
endpoint: HarnessV1PortEndpoint;
|
|
1349
|
-
openTimeoutMs: number;
|
|
1350
|
-
getHelloTimeoutMs: () => number;
|
|
1351
|
-
abortSignal: AbortSignal;
|
|
1352
|
-
onHello: (supportsUserMessageResponses: boolean) => void;
|
|
1353
|
-
}): Promise<WebSocket> {
|
|
1354
|
-
return new Promise<WebSocket>((resolve, reject) => {
|
|
1355
|
-
const abortReason = () =>
|
|
1356
|
-
abortSignal.reason ?? new Error('WebSocket connection aborted');
|
|
1357
|
-
if (abortSignal.aborted) {
|
|
1358
|
-
reject(abortReason());
|
|
1359
|
-
return;
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
const ws = new WebSocket(endpoint.url, {
|
|
1363
|
-
headers: endpoint.headers == null ? undefined : { ...endpoint.headers },
|
|
1364
|
-
});
|
|
1365
|
-
let opened = false;
|
|
1366
|
-
let sawBridgeHello = false;
|
|
1367
|
-
let settled = false;
|
|
1368
|
-
let openTimer: ReturnType<typeof setTimeout> | undefined;
|
|
1369
|
-
let helloTimer: ReturnType<typeof setTimeout> | undefined;
|
|
1370
|
-
let onAbort: (() => void) | undefined;
|
|
1371
|
-
|
|
1372
|
-
const cleanup = ({
|
|
1373
|
-
keepTerminationListeners = false,
|
|
1374
|
-
}: {
|
|
1375
|
-
keepTerminationListeners?: boolean;
|
|
1376
|
-
} = {}) => {
|
|
1377
|
-
if (openTimer) clearTimeout(openTimer);
|
|
1378
|
-
if (helloTimer) clearTimeout(helloTimer);
|
|
1379
|
-
ws.off('open', onOpen);
|
|
1380
|
-
ws.off('message', onMessage);
|
|
1381
|
-
if (onAbort != null) {
|
|
1382
|
-
abortSignal.removeEventListener('abort', onAbort);
|
|
1383
|
-
}
|
|
1384
|
-
if (!keepTerminationListeners) {
|
|
1385
|
-
ws.off('close', onClose);
|
|
1386
|
-
ws.off('error', onError);
|
|
1387
|
-
}
|
|
1388
|
-
};
|
|
1389
|
-
const settle = (err?: unknown) => {
|
|
1390
|
-
if (settled) return;
|
|
1391
|
-
settled = true;
|
|
1392
|
-
if (err) {
|
|
1393
|
-
cleanup({ keepTerminationListeners: true });
|
|
1394
|
-
try {
|
|
1395
|
-
ws.terminate();
|
|
1396
|
-
} catch {
|
|
1397
|
-
cleanup();
|
|
1398
|
-
}
|
|
1399
|
-
reject(err);
|
|
1400
|
-
} else {
|
|
1401
|
-
cleanup();
|
|
1402
|
-
resolve(ws);
|
|
1403
|
-
}
|
|
1404
|
-
};
|
|
1405
|
-
const tryResolve = () => {
|
|
1406
|
-
if (opened && sawBridgeHello) settle();
|
|
1407
|
-
};
|
|
1408
|
-
const startHelloTimer = () => {
|
|
1409
|
-
if (helloTimer) return;
|
|
1410
|
-
const helloTimeoutMs = getHelloTimeoutMs();
|
|
1411
|
-
helloTimer = setTimeout(
|
|
1412
|
-
() =>
|
|
1413
|
-
settle(
|
|
1414
|
-
new Error(
|
|
1415
|
-
`claude-code bridge did not send bridge-hello within ${helloTimeoutMs}ms`,
|
|
1416
|
-
),
|
|
1417
|
-
),
|
|
1418
|
-
helloTimeoutMs,
|
|
1419
|
-
);
|
|
1420
|
-
helloTimer.unref?.();
|
|
1421
|
-
};
|
|
1422
|
-
const onOpen = () => {
|
|
1423
|
-
opened = true;
|
|
1424
|
-
if (openTimer) {
|
|
1425
|
-
clearTimeout(openTimer);
|
|
1426
|
-
openTimer = undefined;
|
|
1427
|
-
}
|
|
1428
|
-
startHelloTimer();
|
|
1429
|
-
tryResolve();
|
|
1430
|
-
};
|
|
1431
|
-
const onMessage = (raw: unknown) => {
|
|
1432
|
-
void (async () => {
|
|
1433
|
-
const parsed = await safeParseJSON({
|
|
1434
|
-
text: webSocketMessageToString(raw),
|
|
1435
|
-
});
|
|
1436
|
-
if (!parsed.success || settled) return;
|
|
1437
|
-
const value = parsed.value;
|
|
1438
|
-
if (
|
|
1439
|
-
typeof value === 'object' &&
|
|
1440
|
-
value !== null &&
|
|
1441
|
-
!Array.isArray(value) &&
|
|
1442
|
-
(value as { type?: unknown }).type === 'bridge-hello'
|
|
1443
|
-
) {
|
|
1444
|
-
const capabilities = (
|
|
1445
|
-
value as {
|
|
1446
|
-
capabilities?: { experimental_userMessageResponses?: unknown };
|
|
1447
|
-
}
|
|
1448
|
-
).capabilities;
|
|
1449
|
-
onHello(capabilities?.experimental_userMessageResponses === true);
|
|
1450
|
-
sawBridgeHello = true;
|
|
1451
|
-
tryResolve();
|
|
1452
|
-
}
|
|
1453
|
-
})();
|
|
1454
|
-
};
|
|
1455
|
-
const onClose = () => {
|
|
1456
|
-
settle(
|
|
1457
|
-
new Error('claude-code bridge closed before sending bridge-hello'),
|
|
1458
|
-
);
|
|
1459
|
-
cleanup();
|
|
1460
|
-
};
|
|
1461
|
-
const onError = (err: Error) => settle(err);
|
|
1462
|
-
onAbort = () => settle(abortReason());
|
|
1463
|
-
openTimer = setTimeout(
|
|
1464
|
-
() =>
|
|
1465
|
-
settle(new Error(`WebSocket open timed out after ${openTimeoutMs}ms`)),
|
|
1466
|
-
openTimeoutMs,
|
|
1467
|
-
);
|
|
1468
|
-
openTimer.unref?.();
|
|
1469
|
-
ws.on('open', onOpen);
|
|
1470
|
-
ws.on('message', onMessage);
|
|
1471
|
-
ws.on('close', onClose);
|
|
1472
|
-
ws.on('error', onError);
|
|
1473
|
-
abortSignal.addEventListener('abort', onAbort, { once: true });
|
|
1474
|
-
});
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
async function openBridgeWebSocket({
|
|
1478
|
-
endpoint,
|
|
1479
|
-
timeoutMs,
|
|
1480
|
-
abortSignal,
|
|
1481
|
-
onHello,
|
|
1482
|
-
}: {
|
|
1483
|
-
endpoint: HarnessV1PortEndpoint;
|
|
1484
|
-
timeoutMs: number;
|
|
1485
|
-
abortSignal: AbortSignal;
|
|
1486
|
-
onHello: (supportsUserMessageResponses: boolean) => void;
|
|
1487
|
-
}): Promise<WebSocket> {
|
|
1488
|
-
const deadline = Date.now() + timeoutMs;
|
|
1489
|
-
let attempt = 0;
|
|
1490
|
-
let lastError: unknown;
|
|
1491
|
-
|
|
1492
|
-
while (!abortSignal.aborted && Date.now() < deadline) {
|
|
1493
|
-
attempt++;
|
|
1494
|
-
try {
|
|
1495
|
-
const remaining = Math.max(1, deadline - Date.now());
|
|
1496
|
-
return await openWebSocketAndWaitForBridgeHello({
|
|
1497
|
-
endpoint,
|
|
1498
|
-
openTimeoutMs: Math.min(10_000, remaining),
|
|
1499
|
-
getHelloTimeoutMs: () =>
|
|
1500
|
-
Math.min(5_000, Math.max(1, deadline - Date.now())),
|
|
1501
|
-
abortSignal,
|
|
1502
|
-
onHello,
|
|
1503
|
-
});
|
|
1504
|
-
} catch (err) {
|
|
1505
|
-
if (abortSignal.aborted) {
|
|
1506
|
-
throw abortSignal.reason ?? new Error('WebSocket connection aborted');
|
|
1507
|
-
}
|
|
1508
|
-
lastError = err;
|
|
1509
|
-
const remaining = deadline - Date.now();
|
|
1510
|
-
if (remaining <= 0) break;
|
|
1511
|
-
await sleep({
|
|
1512
|
-
ms: Math.min(250 * attempt, 1_000, remaining),
|
|
1513
|
-
abortSignal,
|
|
1514
|
-
});
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
if (abortSignal.aborted) {
|
|
1519
|
-
throw abortSignal.reason ?? new Error('WebSocket connection aborted');
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
throw new Error(
|
|
1523
|
-
`claude-code bridge did not complete WebSocket handshake within ${timeoutMs}ms after ${attempt} attempt(s). Last error: ${formatUnknownError(lastError)}`,
|
|
1524
|
-
);
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
function webSocketMessageToString(raw: unknown): string {
|
|
1528
|
-
if (typeof raw === 'string') return raw;
|
|
1529
|
-
if (Buffer.isBuffer(raw)) return raw.toString('utf8');
|
|
1530
|
-
if (Array.isArray(raw)) return Buffer.concat(raw).toString('utf8');
|
|
1531
|
-
if (raw instanceof ArrayBuffer) return Buffer.from(raw).toString('utf8');
|
|
1532
|
-
if (ArrayBuffer.isView(raw)) {
|
|
1533
|
-
return Buffer.from(raw.buffer, raw.byteOffset, raw.byteLength).toString(
|
|
1534
|
-
'utf8',
|
|
1535
|
-
);
|
|
1536
|
-
}
|
|
1537
|
-
return String(raw);
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
function formatUnknownError(error: unknown): string {
|
|
1541
|
-
if (error instanceof Error) return error.message;
|
|
1542
|
-
return String(error);
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
1252
|
function createSession({
|
|
1546
1253
|
sessionId,
|
|
1547
1254
|
channel,
|