@ai-sdk/harness-claude-code 1.0.73 → 1.0.75

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.
@@ -2,9 +2,52 @@ import { execFileSync } from 'node:child_process';
2
2
  import { readFileSync } from 'node:fs';
3
3
  import { homedir } from 'node:os';
4
4
  import { join } from 'node:path';
5
- import { getAiGatewayAuthFromEnv } from '@ai-sdk/harness/utils';
5
+ import type { HarnessV1RequestTransformation } from '@ai-sdk/harness';
6
+ import {
7
+ createCredentialRequestTransformation,
8
+ getAiGatewayAuthFromEnv,
9
+ } from '@ai-sdk/harness/utils';
6
10
 
7
- export type ClaudeCodeAuthOptions = {
11
+ export const CLAUDE_CODE_CREDENTIAL_ENVIRONMENT_VARIABLES = [
12
+ 'AI_GATEWAY_API_KEY',
13
+ 'ANTHROPIC_API_KEY',
14
+ 'ANTHROPIC_AUTH_TOKEN',
15
+ ] as const;
16
+
17
+ export function createClaudeCodeRequestTransformations(
18
+ env: Record<string, string>,
19
+ auth: ClaudeCodeResolvedAuthenticationMode,
20
+ ): HarnessV1RequestTransformation[] {
21
+ const headers: Record<string, string> = {};
22
+ if (env.ANTHROPIC_API_KEY) {
23
+ headers['x-api-key'] = env.ANTHROPIC_API_KEY;
24
+ }
25
+ if (env.ANTHROPIC_AUTH_TOKEN) {
26
+ headers.Authorization = `Bearer ${env.ANTHROPIC_AUTH_TOKEN}`;
27
+ }
28
+ return Object.keys(headers).length === 0
29
+ ? []
30
+ : [
31
+ createCredentialRequestTransformation({
32
+ baseUrl:
33
+ auth === 'ai-gateway'
34
+ ? env.ANTHROPIC_BASE_URL
35
+ : (env.ANTHROPIC_BASE_URL ?? 'https://api.anthropic.com'),
36
+ headers,
37
+ }),
38
+ ];
39
+ }
40
+
41
+ export type ClaudeCodeResolvedAuthenticationMode = 'direct' | 'ai-gateway';
42
+
43
+ export type ClaudeCodeAuthenticationMode =
44
+ | ClaudeCodeResolvedAuthenticationMode
45
+ | 'auto';
46
+
47
+ /**
48
+ * @deprecated Passing an object to auth options is deprecated. Use a `ClaudeCodeAuthenticationMode` string value ("auto" | "direct" | "ai-gateway") instead, and pass credentials via environment variables.
49
+ */
50
+ export type LegacyClaudeCodeAuthOptions = {
8
51
  readonly anthropic?: {
9
52
  readonly apiKey?: string;
10
53
  readonly authToken?: string;
@@ -16,6 +59,10 @@ export type ClaudeCodeAuthOptions = {
16
59
  };
17
60
  };
18
61
 
62
+ export type ClaudeCodeAuthOptions =
63
+ | ClaudeCodeAuthenticationMode
64
+ | LegacyClaudeCodeAuthOptions;
65
+
19
66
  /**
20
67
  * Resolve the environment-variable blob the bridge needs to authenticate
21
68
  * with Anthropic (directly or via the Vercel AI Gateway). Precedence:
@@ -42,15 +89,21 @@ export function resolveClaudeCodeEnv(
42
89
  processEnv: Record<string, string | undefined> = process.env,
43
90
  options: ResolveClaudeCodeEnvOptions = {},
44
91
  ): Record<string, string> {
92
+ const normalizedAuth = normalizeClaudeCodeAuthToLegacyAuth(auth);
93
+
45
94
  const readApiKey = options.readApiKeyHelper ?? readApiKeyHelper;
46
- if (auth?.anthropic) {
47
- return pickAnthropic({ explicit: auth.anthropic, processEnv, readApiKey });
95
+ if (normalizedAuth?.anthropic) {
96
+ return pickAnthropic({
97
+ explicit: normalizedAuth.anthropic,
98
+ processEnv,
99
+ readApiKey,
100
+ });
48
101
  }
49
102
 
50
103
  const gatewayAuthFromEnv = getAiGatewayAuthFromEnv({ env: processEnv });
51
- if (auth?.gateway) {
104
+ if (normalizedAuth?.gateway) {
52
105
  return pickGateway({
53
- explicit: auth.gateway,
106
+ explicit: normalizedAuth.gateway,
54
107
  gatewayAuthFromEnv,
55
108
  });
56
109
  }
@@ -64,12 +117,50 @@ export function resolveClaudeCodeEnv(
64
117
  return pickAnthropic({ processEnv, readApiKey });
65
118
  }
66
119
 
120
+ export function resolveClaudeCodeAuthenticationMode(
121
+ auth: ClaudeCodeAuthOptions | undefined,
122
+ processEnv: Record<string, string | undefined> = process.env,
123
+ ): ClaudeCodeResolvedAuthenticationMode {
124
+ if (auth === 'direct' || (typeof auth !== 'string' && auth?.anthropic)) {
125
+ return 'direct';
126
+ }
127
+ if (auth === 'ai-gateway' || (typeof auth !== 'string' && auth?.gateway)) {
128
+ return 'ai-gateway';
129
+ }
130
+ return getAiGatewayAuthFromEnv({ env: processEnv }).apiKey
131
+ ? 'ai-gateway'
132
+ : 'direct';
133
+ }
134
+
135
+ function normalizeClaudeCodeAuthToLegacyAuth(
136
+ auth: ClaudeCodeAuthOptions | undefined,
137
+ ): LegacyClaudeCodeAuthOptions | undefined {
138
+ if (auth == null || auth === 'auto') {
139
+ return undefined;
140
+ }
141
+ if (typeof auth === 'string') {
142
+ switch (auth) {
143
+ case 'direct':
144
+ return { anthropic: {} };
145
+ case 'ai-gateway':
146
+ return { gateway: {} };
147
+ default:
148
+ return undefined;
149
+ }
150
+ }
151
+
152
+ console.warn(
153
+ '[claude-code] Passing an object to auth options is deprecated. Use a string mode ("auto" | "direct" | "ai-gateway") instead, and pass credentials via environment variables.',
154
+ );
155
+ return auth;
156
+ }
157
+
67
158
  function pickAnthropic({
68
159
  explicit,
69
160
  processEnv,
70
161
  readApiKey,
71
162
  }: {
72
- explicit?: NonNullable<ClaudeCodeAuthOptions['anthropic']>;
163
+ explicit?: NonNullable<LegacyClaudeCodeAuthOptions['anthropic']>;
73
164
  processEnv: Record<string, string | undefined>;
74
165
  readApiKey: () => string | undefined;
75
166
  }): Record<string, string> {
@@ -125,7 +216,7 @@ function pickGateway({
125
216
  explicit,
126
217
  gatewayAuthFromEnv,
127
218
  }: {
128
- explicit: NonNullable<ClaudeCodeAuthOptions['gateway']>;
219
+ explicit: NonNullable<LegacyClaudeCodeAuthOptions['gateway']>;
129
220
  gatewayAuthFromEnv: ReturnType<typeof getAiGatewayAuthFromEnv>;
130
221
  }): Record<string, string> {
131
222
  const apiKey = explicit.apiKey ?? gatewayAuthFromEnv.apiKey;
@@ -0,0 +1,65 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { fileURLToPath } from 'node:url';
3
+ import type { HarnessV1Bootstrap } from '@ai-sdk/harness';
4
+
5
+ /*
6
+ * Bootstrap is derived state stored under the sandbox's default working
7
+ * directory so snapshot-capable providers can preserve the installed CLI,
8
+ * bridge, and recipe marker without requiring root filesystem access.
9
+ *
10
+ * The session work dir (`startOpts.sessionWorkDir`) and the bridge-state dir
11
+ * derived from `sandboxSession.defaultWorkingDirectory` both live under the sandbox's
12
+ * default working directory — the provider's persistent mount — so the
13
+ * workdir's CLI state (Claude's `~/.claude/projects/<dir>/*.jsonl` thread
14
+ * history is keyed by working directory) and the bridge state files survive
15
+ * both detach -> attach/replay and stop -> snapshot -> resume cycles.
16
+ */
17
+ export const CLAUDE_CODE_BOOTSTRAP_DIR = '.harness-bootstrap/claude-code';
18
+
19
+ let cachedBootstrap: HarnessV1Bootstrap | undefined;
20
+
21
+ export async function getClaudeCodeBootstrap(): Promise<HarnessV1Bootstrap> {
22
+ if (cachedBootstrap != null) return cachedBootstrap;
23
+ const [pkg, lock, bridge] = await Promise.all([
24
+ readBridgeAsset('package.json'),
25
+ readBridgeAsset('pnpm-lock.yaml'),
26
+ readBridgeAsset('index.mjs'),
27
+ ]);
28
+ cachedBootstrap = {
29
+ harnessId: 'claude-code',
30
+ bootstrapDir: CLAUDE_CODE_BOOTSTRAP_DIR,
31
+ files: [
32
+ { path: `${CLAUDE_CODE_BOOTSTRAP_DIR}/package.json`, content: pkg },
33
+ { path: `${CLAUDE_CODE_BOOTSTRAP_DIR}/pnpm-lock.yaml`, content: lock },
34
+ { path: `${CLAUDE_CODE_BOOTSTRAP_DIR}/bridge.mjs`, content: bridge },
35
+ ],
36
+ commands: [
37
+ {
38
+ command: 'pnpm install --frozen-lockfile --store-dir .pnpm-store',
39
+ },
40
+ {
41
+ command:
42
+ 'if [ -f node_modules/@anthropic-ai/claude-code/install.cjs ]; then node node_modules/@anthropic-ai/claude-code/install.cjs; fi && ./node_modules/.bin/claude --version',
43
+ },
44
+ ],
45
+ };
46
+ return cachedBootstrap;
47
+ }
48
+
49
+ async function readBridgeAsset(name: string): Promise<string> {
50
+ const candidates = [
51
+ new URL(`./bridge/${name}`, import.meta.url),
52
+ new URL(`../bridge/${name}`, import.meta.url),
53
+ ];
54
+ let lastErr: unknown;
55
+ for (const url of candidates) {
56
+ try {
57
+ return await readFile(fileURLToPath(url), 'utf8');
58
+ } catch (err) {
59
+ const code = (err as NodeJS.ErrnoException).code;
60
+ if (code !== 'ENOENT') throw err;
61
+ lastErr = err;
62
+ }
63
+ }
64
+ throw lastErr ?? new Error(`bridge asset not found: ${name}`);
65
+ }
@@ -1,13 +1,10 @@
1
1
  import { randomBytes } from 'node:crypto';
2
- import { readFile } from 'node:fs/promises';
3
2
  import { posix } from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
3
  import {
6
4
  commonTool,
7
5
  harnessV1DiagnosticFromBridgeFrame,
8
6
  HarnessCapabilityUnsupportedError,
9
7
  type HarnessV1,
10
- type HarnessV1Bootstrap,
11
8
  type HarnessV1BuiltinToolFiltering,
12
9
  type HarnessV1BuiltinTool,
13
10
  type HarnessV1ContinueTurnState,
@@ -15,6 +12,7 @@ import {
15
12
  type HarnessV1PermissionMode,
16
13
  type HarnessV1Prompt,
17
14
  type HarnessV1PromptControl,
15
+ type HarnessV1PortEndpoint,
18
16
  type HarnessV1ResumeSessionState,
19
17
  type HarnessV1NetworkSandboxSession,
20
18
  type HarnessV1Session,
@@ -28,9 +26,11 @@ import {
28
26
  drainBridgeProcessStream,
29
27
  forwardBridgeProcessStream,
30
28
  markBridgeStarting,
29
+ maskSandboxCredentials,
31
30
  resolveSandboxHomeDir,
32
31
  SandboxChannel,
33
32
  shellQuote,
33
+ warnCredentialBrokeringUnavailable,
34
34
  waitForBridgeReady,
35
35
  writeSkills as writeHarnessSkills,
36
36
  } from '@ai-sdk/harness/utils';
@@ -43,6 +43,13 @@ import {
43
43
  import { WebSocket } from 'ws';
44
44
  import { z } from 'zod/v4';
45
45
  import {
46
+ CLAUDE_CODE_BOOTSTRAP_DIR as BOOTSTRAP_DIR,
47
+ getClaudeCodeBootstrap,
48
+ } from './claude-code-bootstrap';
49
+ import {
50
+ CLAUDE_CODE_CREDENTIAL_ENVIRONMENT_VARIABLES,
51
+ createClaudeCodeRequestTransformations,
52
+ resolveClaudeCodeAuthenticationMode,
46
53
  resolveClaudeCodeEnv,
47
54
  type ClaudeCodeAuthOptions,
48
55
  } from './claude-code-auth';
@@ -753,20 +760,6 @@ const CLAUDE_CODE_BUILTIN_TOOLS = {
753
760
  },
754
761
  } as const satisfies Record<string, HarnessV1BuiltinTool<any, any>>;
755
762
 
756
- /*
757
- * Bootstrap is derived state stored under the sandbox's default working
758
- * directory so snapshot-capable providers can preserve the installed CLI,
759
- * bridge, and recipe marker without requiring root filesystem access.
760
- *
761
- * The session work dir (`startOpts.sessionWorkDir`) and the bridge-state dir
762
- * derived from `sandboxSession.defaultWorkingDirectory` both live under the sandbox's
763
- * default working directory — the provider's persistent mount — so the
764
- * workdir's CLI state (Claude's `~/.claude/projects/<dir>/*.jsonl` thread
765
- * history is keyed by working directory) and the bridge state files survive
766
- * both detach -> attach/replay and stop -> snapshot -> resume cycles.
767
- */
768
- const BOOTSTRAP_DIR = '.harness-bootstrap/claude-code';
769
-
770
763
  /**
771
764
  * Live bridge coordinates returned by `doDetach()` and `doSuspendTurn()`. A
772
765
  * future process uses them to reopen a socket to the still-running bridge
@@ -805,7 +798,6 @@ export function createClaudeCode(
805
798
  'Claude Code MCP server name "harness-tools" is reserved for HarnessAgent tools.',
806
799
  );
807
800
  }
808
- let cachedBootstrap: HarnessV1Bootstrap | undefined;
809
801
  const thinking = settings.thinking ?? {
810
802
  type: 'adaptive',
811
803
  display: 'summarized',
@@ -818,35 +810,41 @@ export function createClaudeCode(
818
810
  supportsBuiltinToolApprovals: true,
819
811
  supportsBuiltinToolFiltering: true,
820
812
  lifecycleStateSchema: claudeCodeResumeStateSchema,
821
- getBootstrap: async () => {
822
- if (cachedBootstrap != null) return cachedBootstrap;
823
- const [pkg, lock, bridge] = await Promise.all([
824
- readBridgeAsset('package.json'),
825
- readBridgeAsset('pnpm-lock.yaml'),
826
- readBridgeAsset('index.mjs'),
827
- ]);
828
- cachedBootstrap = {
829
- harnessId: 'claude-code',
830
- bootstrapDir: BOOTSTRAP_DIR,
831
- files: [
832
- { path: `${BOOTSTRAP_DIR}/package.json`, content: pkg },
833
- { path: `${BOOTSTRAP_DIR}/pnpm-lock.yaml`, content: lock },
834
- { path: `${BOOTSTRAP_DIR}/bridge.mjs`, content: bridge },
835
- ],
836
- commands: [
837
- {
838
- command: 'pnpm install --frozen-lockfile --store-dir .pnpm-store',
839
- },
840
- {
841
- command:
842
- 'if [ -f node_modules/@anthropic-ai/claude-code/install.cjs ]; then node node_modules/@anthropic-ai/claude-code/install.cjs; fi && ./node_modules/.bin/claude --version',
843
- },
844
- ],
845
- };
846
- return cachedBootstrap;
847
- },
813
+ getBootstrap: getClaudeCodeBootstrap,
848
814
  doStart: async startOpts => {
849
815
  const sandboxSession = startOpts.sandboxSession;
816
+ const authenticationMode = resolveClaudeCodeAuthenticationMode(
817
+ settings.auth,
818
+ );
819
+ const resolvedAuthEnvironment = resolveClaudeCodeEnv(settings.auth);
820
+ let authEnv = resolvedAuthEnvironment;
821
+ let sandboxTurnEnvironment = settings.env;
822
+ if (sandboxSession.addRequestTransformations != null) {
823
+ const requestTransformations = createClaudeCodeRequestTransformations(
824
+ {
825
+ ...resolvedAuthEnvironment,
826
+ ...settings.env,
827
+ },
828
+ authenticationMode,
829
+ );
830
+ if (requestTransformations.length > 0) {
831
+ await sandboxSession.addRequestTransformations(
832
+ requestTransformations,
833
+ );
834
+ }
835
+ authEnv = maskSandboxCredentials({
836
+ environment: resolvedAuthEnvironment,
837
+ credentialEnvironmentVariables:
838
+ CLAUDE_CODE_CREDENTIAL_ENVIRONMENT_VARIABLES,
839
+ });
840
+ sandboxTurnEnvironment = maskSandboxCredentials({
841
+ environment: settings.env ?? {},
842
+ credentialEnvironmentVariables:
843
+ CLAUDE_CODE_CREDENTIAL_ENVIRONMENT_VARIABLES,
844
+ });
845
+ } else {
846
+ warnCredentialBrokeringUnavailable();
847
+ }
850
848
  const session = sandboxSession.restricted();
851
849
  const sandboxId = sandboxSession.id;
852
850
  const bootstrapDir = posix.resolve(
@@ -886,9 +884,10 @@ export function createClaudeCode(
886
884
  // Builds the `connect` thunk a `SandboxChannel` re-invokes on every
887
885
  // (re)connect: open the socket, then wait for `bridge-hello` so the
888
886
  // end-to-end link is proven live before any frame is sent.
889
- const buildConnect = (wsUrl: string) => async (): Promise<WebSocket> => {
890
- return openBridgeWebSocket({ wsUrl, timeoutMs });
891
- };
887
+ const buildConnect =
888
+ (endpoint: HarnessV1PortEndpoint) => async (): Promise<WebSocket> => {
889
+ return openBridgeWebSocket({ endpoint, timeoutMs });
890
+ };
892
891
 
893
892
  /*
894
893
  * Rung 1 — ATTACH. When lifecycle state carries live bridge coordinates,
@@ -900,13 +899,16 @@ export function createClaudeCode(
900
899
  */
901
900
  if (coords) {
902
901
  try {
903
- const attachUrl =
904
- (await sandboxSession.getPortUrl({
905
- port: coords.port,
906
- protocol: 'ws',
907
- })) + `?agent_bridge_token=${encodeURIComponent(coords.token)}`;
902
+ const endpoint = await sandboxSession.getPortEndpoint({
903
+ port: coords.port,
904
+ protocol: 'ws',
905
+ });
906
+ const attachEndpoint = withBridgeToken({
907
+ endpoint,
908
+ token: coords.token,
909
+ });
908
910
  const attachChannel: ClaudeCodeChannel = new SandboxChannel({
909
- connect: buildConnect(attachUrl),
911
+ connect: buildConnect(attachEndpoint),
910
912
  outboundSchema: outboundMessageSchema,
911
913
  initialLastSeenEventId: coords.lastSeenEventId,
912
914
  onDiagnostic,
@@ -922,7 +924,7 @@ export function createClaudeCode(
922
924
  proc: undefined,
923
925
  model: settings.model,
924
926
  maxTurns: settings.maxTurns,
925
- env: settings.env,
927
+ env: sandboxTurnEnvironment,
926
928
  thinking,
927
929
  effort: settings.effort,
928
930
  isResume: true,
@@ -978,7 +980,6 @@ export function createClaudeCode(
978
980
  settings.mintBridgeToken == null
979
981
  ? randomBytes(32).toString('hex')
980
982
  : settings.mintBridgeToken(sandboxId);
981
- const authEnv = resolveClaudeCodeEnv(settings.auth);
982
983
  const env = {
983
984
  ...authEnv,
984
985
  /*
@@ -1070,14 +1071,14 @@ export function createClaudeCode(
1070
1071
  });
1071
1072
  void drainBridgeProcessStream(proc.stdout);
1072
1073
 
1073
- const wsUrl =
1074
- (await sandboxSession.getPortUrl({
1075
- port: boundPort,
1076
- protocol: 'ws',
1077
- })) + `?agent_bridge_token=${encodeURIComponent(token)}`;
1074
+ const endpoint = await sandboxSession.getPortEndpoint({
1075
+ port: boundPort,
1076
+ protocol: 'ws',
1077
+ });
1078
+ const bridgeEndpoint = withBridgeToken({ endpoint, token });
1078
1079
 
1079
1080
  const channel: ClaudeCodeChannel = new SandboxChannel({
1080
- connect: buildConnect(wsUrl),
1081
+ connect: buildConnect(bridgeEndpoint),
1081
1082
  outboundSchema: outboundMessageSchema,
1082
1083
  onDiagnostic,
1083
1084
  onBridgeError,
@@ -1098,7 +1099,7 @@ export function createClaudeCode(
1098
1099
  proc,
1099
1100
  model: settings.model,
1100
1101
  maxTurns: settings.maxTurns,
1101
- env: settings.env,
1102
+ env: sandboxTurnEnvironment,
1102
1103
  thinking,
1103
1104
  effort: settings.effort,
1104
1105
  isResume: respawnStrategy !== undefined,
@@ -1162,24 +1163,6 @@ async function writeClaudeCodeSkills({
1162
1163
  });
1163
1164
  }
1164
1165
 
1165
- async function readBridgeAsset(name: string): Promise<string> {
1166
- const candidates = [
1167
- new URL(`./bridge/${name}`, import.meta.url),
1168
- new URL(`../bridge/${name}`, import.meta.url),
1169
- ];
1170
- let lastErr: unknown;
1171
- for (const url of candidates) {
1172
- try {
1173
- return await readFile(fileURLToPath(url), 'utf8');
1174
- } catch (err) {
1175
- const code = (err as NodeJS.ErrnoException).code;
1176
- if (code !== 'ENOENT') throw err;
1177
- lastErr = err;
1178
- }
1179
- }
1180
- throw lastErr ?? new Error(`bridge asset not found: ${name}`);
1181
- }
1182
-
1183
1166
  /**
1184
1167
  * Wait for the bridge's `bridge-hello` message to arrive on the freshly
1185
1168
  * opened WebSocket before any other host-side code touches it.
@@ -1192,16 +1175,18 @@ async function readBridgeAsset(name: string): Promise<string> {
1192
1175
  * is the only reliable evidence that the end-to-end link is live.
1193
1176
  */
1194
1177
  function openWebSocketAndWaitForBridgeHello({
1195
- url,
1178
+ endpoint,
1196
1179
  openTimeoutMs,
1197
1180
  getHelloTimeoutMs,
1198
1181
  }: {
1199
- url: string;
1182
+ endpoint: HarnessV1PortEndpoint;
1200
1183
  openTimeoutMs: number;
1201
1184
  getHelloTimeoutMs: () => number;
1202
1185
  }): Promise<WebSocket> {
1203
1186
  return new Promise<WebSocket>((resolve, reject) => {
1204
- const ws = new WebSocket(url);
1187
+ const ws = new WebSocket(endpoint.url, {
1188
+ headers: endpoint.headers == null ? undefined : { ...endpoint.headers },
1189
+ });
1205
1190
  let opened = false;
1206
1191
  let sawBridgeHello = false;
1207
1192
  let settled = false;
@@ -1293,10 +1278,10 @@ function openWebSocketAndWaitForBridgeHello({
1293
1278
  }
1294
1279
 
1295
1280
  async function openBridgeWebSocket({
1296
- wsUrl,
1281
+ endpoint,
1297
1282
  timeoutMs,
1298
1283
  }: {
1299
- wsUrl: string;
1284
+ endpoint: HarnessV1PortEndpoint;
1300
1285
  timeoutMs: number;
1301
1286
  }): Promise<WebSocket> {
1302
1287
  const deadline = Date.now() + timeoutMs;
@@ -1308,7 +1293,7 @@ async function openBridgeWebSocket({
1308
1293
  try {
1309
1294
  const remaining = Math.max(1, deadline - Date.now());
1310
1295
  return await openWebSocketAndWaitForBridgeHello({
1311
- url: wsUrl,
1296
+ endpoint,
1312
1297
  openTimeoutMs: Math.min(10_000, remaining),
1313
1298
  getHelloTimeoutMs: () =>
1314
1299
  Math.min(5_000, Math.max(1, deadline - Date.now())),
@@ -1339,6 +1324,18 @@ function webSocketMessageToString(raw: unknown): string {
1339
1324
  return String(raw);
1340
1325
  }
1341
1326
 
1327
+ function withBridgeToken({
1328
+ endpoint,
1329
+ token,
1330
+ }: {
1331
+ endpoint: HarnessV1PortEndpoint;
1332
+ token: string;
1333
+ }): HarnessV1PortEndpoint {
1334
+ const bridgeUrl = new URL(endpoint.url);
1335
+ bridgeUrl.searchParams.set('agent_bridge_token', token);
1336
+ return { ...endpoint, url: bridgeUrl.toString() };
1337
+ }
1338
+
1342
1339
  function sleep(ms: number): Promise<void> {
1343
1340
  return new Promise(resolve => {
1344
1341
  const timer = setTimeout(resolve, ms);
package/src/index.ts CHANGED
@@ -10,5 +10,8 @@ export const claudeCode = createClaudeCode();
10
10
  export { createClaudeCode } from './claude-code-harness';
11
11
  export { VERSION } from './version';
12
12
  export type { ClaudeCodeHarnessSettings } from './claude-code-harness';
13
- export type { ClaudeCodeAuthOptions } from './claude-code-auth';
13
+ export type {
14
+ ClaudeCodeAuthenticationMode,
15
+ ClaudeCodeAuthOptions,
16
+ } from './claude-code-auth';
14
17
  export type { ClaudeCodeThinkingConfig } from './claude-code-thinking';