@ai-sdk/harness-claude-code 1.0.111 → 1.0.112

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 CHANGED
@@ -1,5 +1,19 @@
1
1
  # @ai-sdk/harness-claude-code
2
2
 
3
+ ## 1.0.112
4
+
5
+ ### Patch Changes
6
+
7
+ - cdc12a1: feat(harness): add support for using adapter-native subscriptions where available
8
+ - Updated dependencies [4d1bf28]
9
+ - Updated dependencies [cdc12a1]
10
+ - Updated dependencies [d70a334]
11
+ - Updated dependencies [81ba84c]
12
+ - Updated dependencies [813bb36]
13
+ - Updated dependencies [c43e4b7]
14
+ - @ai-sdk/harness@1.0.108
15
+ - @ai-sdk/provider-utils@5.0.40
16
+
3
17
  ## 1.0.111
4
18
 
5
19
  ### Patch Changes
package/dist/index.js CHANGED
@@ -28,7 +28,7 @@ import {
28
28
  writeSkills
29
29
  } from "@ai-sdk/harness/utils";
30
30
  import {
31
- safeParseJSON,
31
+ safeParseJSON as safeParseJSON2,
32
32
  tool
33
33
  } from "@ai-sdk/provider-utils";
34
34
  import { WebSocket } from "ws";
@@ -86,7 +86,8 @@ import {
86
86
  var CLAUDE_CODE_CREDENTIAL_ENVIRONMENT_VARIABLES = [
87
87
  "AI_GATEWAY_API_KEY",
88
88
  "ANTHROPIC_API_KEY",
89
- "ANTHROPIC_AUTH_TOKEN"
89
+ "ANTHROPIC_AUTH_TOKEN",
90
+ "CLAUDE_CODE_OAUTH_TOKEN"
90
91
  ];
91
92
  function createClaudeCodeRequestTransformations({
92
93
  env: environment,
@@ -164,8 +165,10 @@ function pickAnthropic({
164
165
  const helperKey = readApiKey();
165
166
  const apiKey = processEnv.ANTHROPIC_API_KEY ?? helperKey;
166
167
  const authToken = processEnv.ANTHROPIC_AUTH_TOKEN ?? helperKey;
168
+ const oauthToken = processEnv.CLAUDE_CODE_OAUTH_TOKEN;
167
169
  if (apiKey) env.ANTHROPIC_API_KEY = apiKey;
168
170
  if (authToken) env.ANTHROPIC_AUTH_TOKEN = authToken;
171
+ if (oauthToken) env.CLAUDE_CODE_OAUTH_TOKEN = oauthToken;
169
172
  const baseUrl = processEnv.ANTHROPIC_BASE_URL;
170
173
  if (baseUrl) env.ANTHROPIC_BASE_URL = baseUrl;
171
174
  return env;
@@ -213,6 +216,184 @@ function pickGateway({
213
216
  return env;
214
217
  }
215
218
 
219
+ // src/claude-code-subscription.ts
220
+ import { execFileSync as execFileSync2 } from "child_process";
221
+ import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
222
+ import { homedir as homedir2 } from "os";
223
+ import { dirname, join as join2, resolve } from "path";
224
+ import {
225
+ createCredentialRequestTransformation as createCredentialRequestTransformation2,
226
+ isAccessTokenExpiringSoon,
227
+ isHarnessAuthenticationEnvironment as isHarnessAuthenticationEnvironment2,
228
+ isMacOS,
229
+ readMacOSKeychainPassword,
230
+ refreshOAuthAccessToken,
231
+ shouldResolveNativeSubscription
232
+ } from "@ai-sdk/harness/utils";
233
+ import { isRecord, safeParseJSON } from "@ai-sdk/provider-utils";
234
+ var CLAUDE_TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
235
+ var CLAUDE_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
236
+ function createClaudeCodeSubscriptionRequestTransformations({
237
+ env: environment,
238
+ sandboxEnv: sandboxEnvironment,
239
+ auth: authenticationMode
240
+ }) {
241
+ if (environment.CLAUDE_CODE_OAUTH_TOKEN == null || sandboxEnvironment.CLAUDE_CODE_OAUTH_TOKEN == null) {
242
+ return [];
243
+ }
244
+ return [
245
+ createCredentialRequestTransformation2({
246
+ matchUrl: authenticationMode === "ai-gateway" ? environment.ANTHROPIC_BASE_URL : environment.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com",
247
+ matchHeaders: {
248
+ Authorization: `Bearer ${sandboxEnvironment.CLAUDE_CODE_OAUTH_TOKEN}`
249
+ },
250
+ transformHeaders: {
251
+ Authorization: `Bearer ${environment.CLAUDE_CODE_OAUTH_TOKEN}`
252
+ }
253
+ })
254
+ ];
255
+ }
256
+ async function resolveClaudeCodeAuthentication({
257
+ auth,
258
+ processEnv = process.env,
259
+ options = {},
260
+ readSubscription = readClaudeCodeSubscription
261
+ }) {
262
+ const environmentWithoutHelper = resolveClaudeCodeEnv(auth, processEnv, {
263
+ readApiKeyHelper: () => void 0
264
+ });
265
+ if (isHarnessAuthenticationEnvironment2(auth)) {
266
+ return environmentWithoutHelper;
267
+ }
268
+ if (!shouldResolveNativeSubscription({
269
+ auth,
270
+ env: environmentWithoutHelper,
271
+ hasDirectCredential: environmentWithoutHelper.ANTHROPIC_API_KEY != null || environmentWithoutHelper.ANTHROPIC_AUTH_TOKEN != null || environmentWithoutHelper.CLAUDE_CODE_OAUTH_TOKEN != null
272
+ })) {
273
+ return environmentWithoutHelper;
274
+ }
275
+ return await readSubscription() ?? resolveClaudeCodeEnv(auth, processEnv, options);
276
+ }
277
+ async function readClaudeCodeSubscription({
278
+ env = process.env,
279
+ homeDirectory = homedir2(),
280
+ platform = process.platform,
281
+ fetch
282
+ } = {}) {
283
+ const configDirectory = resolve(
284
+ env.CLAUDE_CONFIG_DIR ?? join2(homeDirectory, ".claude")
285
+ );
286
+ const credentialPath = join2(configDirectory, ".credentials.json");
287
+ const stored = await readClaudeCredentialStore({
288
+ credentialPath,
289
+ homeDirectory,
290
+ configDirectory,
291
+ platform
292
+ });
293
+ if (stored == null) return void 0;
294
+ const oauth = getClaudeOAuthCredential({ value: stored.value });
295
+ if (oauth == null) return void 0;
296
+ const { accessToken, refreshToken, expiresAt } = oauth;
297
+ let resolvedAccessToken = accessToken;
298
+ if (isAccessTokenExpiringSoon({ expiresAt })) {
299
+ const refreshed = await refreshOAuthAccessToken({
300
+ tokenUrl: CLAUDE_TOKEN_URL,
301
+ clientId: CLAUDE_CLIENT_ID,
302
+ refreshToken,
303
+ requestFormat: "json",
304
+ ...fetch == null ? {} : { fetch }
305
+ });
306
+ resolvedAccessToken = refreshed.accessToken;
307
+ await stored.write({
308
+ ...stored.value,
309
+ claudeAiOauth: {
310
+ ...oauth,
311
+ accessToken: refreshed.accessToken,
312
+ refreshToken: refreshed.refreshToken ?? refreshToken,
313
+ expiresAt: refreshed.expiresAt
314
+ }
315
+ });
316
+ }
317
+ return {
318
+ CLAUDE_CODE_OAUTH_TOKEN: resolvedAccessToken,
319
+ ANTHROPIC_BASE_URL: "https://api.anthropic.com"
320
+ };
321
+ }
322
+ async function readClaudeCredentialStore({
323
+ credentialPath,
324
+ homeDirectory,
325
+ configDirectory,
326
+ platform
327
+ }) {
328
+ const text = await readFile(credentialPath, "utf8").catch(() => void 0);
329
+ if (text != null) {
330
+ const value2 = await parseClaudeCredentialFile(text);
331
+ if (value2 != null && getClaudeOAuthCredential({ value: value2 }) != null) {
332
+ return {
333
+ value: value2,
334
+ write: (updated) => writeClaudeCredentialFile({ credentialPath, value: updated })
335
+ };
336
+ }
337
+ }
338
+ if (!isMacOS(platform)) return void 0;
339
+ if (configDirectory !== resolve(join2(homeDirectory, ".claude"))) {
340
+ return void 0;
341
+ }
342
+ const service = "Claude Code-credentials";
343
+ const keychainText = await readMacOSKeychainPassword({
344
+ service,
345
+ account: process.env.USER ?? ""
346
+ });
347
+ if (keychainText == null) return void 0;
348
+ const value = await parseClaudeCredentialFile(keychainText);
349
+ if (value == null) return void 0;
350
+ return {
351
+ value,
352
+ write: async (updated) => {
353
+ execFileSync2(
354
+ "/usr/bin/security",
355
+ [
356
+ "add-generic-password",
357
+ "-U",
358
+ "-s",
359
+ service,
360
+ "-a",
361
+ process.env.USER ?? "",
362
+ "-w",
363
+ JSON.stringify(updated)
364
+ ],
365
+ { stdio: "ignore" }
366
+ );
367
+ }
368
+ };
369
+ }
370
+ function getClaudeOAuthCredential({
371
+ value
372
+ }) {
373
+ const oauth = value.claudeAiOauth;
374
+ if (!isRecord(oauth) || typeof oauth.accessToken !== "string" || typeof oauth.refreshToken !== "string" || typeof oauth.expiresAt !== "number") {
375
+ return void 0;
376
+ }
377
+ return oauth;
378
+ }
379
+ async function parseClaudeCredentialFile(text) {
380
+ const parsed = await safeParseJSON({ text });
381
+ return parsed.success && isRecord(parsed.value) ? parsed.value : void 0;
382
+ }
383
+ async function writeClaudeCredentialFile({
384
+ credentialPath,
385
+ value
386
+ }) {
387
+ await mkdir(dirname(credentialPath), { recursive: true });
388
+ const temporaryPath = `${credentialPath}.${process.pid}.tmp`;
389
+ await writeFile(temporaryPath, `${JSON.stringify(value, null, 2)}
390
+ `, {
391
+ mode: 384
392
+ });
393
+ await chmod(temporaryPath, 384);
394
+ await rename(temporaryPath, credentialPath);
395
+ }
396
+
216
397
  // src/claude-code-bridge-protocol.ts
217
398
  import {
218
399
  harnessV1BridgeInboundCommandSchemas,
@@ -261,7 +442,7 @@ var inboundMessageSchema = z.discriminatedUnion("type", [
261
442
  ]);
262
443
 
263
444
  // src/version.ts
264
- var VERSION = true ? "1.0.111" : "0.0.0-test";
445
+ var VERSION = true ? "1.0.112" : "0.0.0-test";
265
446
 
266
447
  // src/claude-code-harness.ts
267
448
  var CLAUDE_CODE_CLIENT_APP = `ai-sdk/harness-claude-code/${VERSION}`;
@@ -901,7 +1082,9 @@ function createClaudeCode(settings = {}) {
901
1082
  const authenticationMode = resolveClaudeCodeAuthenticationMode(
902
1083
  settings.auth
903
1084
  );
904
- const resolvedAuthEnvironment = resolveClaudeCodeEnv(settings.auth);
1085
+ const resolvedAuthEnvironment = await resolveClaudeCodeAuthentication({
1086
+ auth: settings.auth
1087
+ });
905
1088
  const claudeEnvironment = {
906
1089
  ...resolvedAuthEnvironment,
907
1090
  /*
@@ -928,11 +1111,17 @@ function createClaudeCode(settings = {}) {
928
1111
  ...claudeEnvironment,
929
1112
  ...sandboxCredentialEnvironment
930
1113
  };
931
- const requestTransformations = createClaudeCodeRequestTransformations({
1114
+ const transformationSources = {
932
1115
  env: claudeEnvironment,
933
1116
  sandboxEnv: sandboxClaudeEnvironment,
934
1117
  auth: authenticationMode
935
- });
1118
+ };
1119
+ const requestTransformations = [
1120
+ ...createClaudeCodeRequestTransformations(transformationSources),
1121
+ ...createClaudeCodeSubscriptionRequestTransformations(
1122
+ transformationSources
1123
+ )
1124
+ ];
936
1125
  if (requestTransformations.length > 0) {
937
1126
  await sandboxSession.addRequestTransformations(
938
1127
  requestTransformations
@@ -1201,7 +1390,7 @@ function openWebSocketAndWaitForBridgeHello({
1201
1390
  getHelloTimeoutMs,
1202
1391
  onHello
1203
1392
  }) {
1204
- return new Promise((resolve, reject) => {
1393
+ return new Promise((resolve2, reject) => {
1205
1394
  const ws = new WebSocket(endpoint.url, {
1206
1395
  headers: endpoint.headers == null ? void 0 : { ...endpoint.headers }
1207
1396
  });
@@ -1235,7 +1424,7 @@ function openWebSocketAndWaitForBridgeHello({
1235
1424
  reject(err);
1236
1425
  } else {
1237
1426
  cleanup();
1238
- resolve(ws);
1427
+ resolve2(ws);
1239
1428
  }
1240
1429
  };
1241
1430
  const tryResolve = () => {
@@ -1265,7 +1454,7 @@ function openWebSocketAndWaitForBridgeHello({
1265
1454
  };
1266
1455
  const onMessage = (raw) => {
1267
1456
  void (async () => {
1268
- const parsed = await safeParseJSON({
1457
+ const parsed = await safeParseJSON2({
1269
1458
  text: webSocketMessageToString(raw)
1270
1459
  });
1271
1460
  if (!parsed.success || settled) return;
@@ -1338,8 +1527,8 @@ function webSocketMessageToString(raw) {
1338
1527
  return String(raw);
1339
1528
  }
1340
1529
  function sleep(ms) {
1341
- return new Promise((resolve) => {
1342
- const timer = setTimeout(resolve, ms);
1530
+ return new Promise((resolve2) => {
1531
+ const timer = setTimeout(resolve2, ms);
1343
1532
  timer.unref?.();
1344
1533
  });
1345
1534
  }
@@ -1378,8 +1567,8 @@ function createSession({
1378
1567
  const wireTurn = (turnOpts) => {
1379
1568
  let pendingResolve;
1380
1569
  let pendingReject;
1381
- const done = new Promise((resolve, reject) => {
1382
- pendingResolve = resolve;
1570
+ const done = new Promise((resolve2, reject) => {
1571
+ pendingResolve = resolve2;
1383
1572
  pendingReject = reject;
1384
1573
  });
1385
1574
  const userMessageSubmitter = supportsUserMessageResponses() ? experimental_createBridgeUserMessageSubmitter({
@@ -1642,8 +1831,8 @@ function createSession({
1642
1831
  if (proc) {
1643
1832
  await Promise.race([
1644
1833
  proc.wait(),
1645
- new Promise((resolve) => {
1646
- stopTimer = setTimeout(resolve, 5e3);
1834
+ new Promise((resolve2) => {
1835
+ stopTimer = setTimeout(resolve2, 5e3);
1647
1836
  stopTimer.unref?.();
1648
1837
  })
1649
1838
  ]);
@@ -1667,7 +1856,7 @@ function createSession({
1667
1856
  }
1668
1857
  stopped = true;
1669
1858
  channel.beginClose();
1670
- const data = channel.isClosed() ? {} : await new Promise((resolve, reject) => {
1859
+ const data = channel.isClosed() ? {} : await new Promise((resolve2, reject) => {
1671
1860
  const timer = setTimeout(() => {
1672
1861
  unsub();
1673
1862
  reject(
@@ -1680,7 +1869,7 @@ function createSession({
1680
1869
  const unsub = channel.on("bridge-stop", (msg) => {
1681
1870
  clearTimeout(timer);
1682
1871
  unsub();
1683
- resolve(msg.data);
1872
+ resolve2(msg.data);
1684
1873
  });
1685
1874
  try {
1686
1875
  channel.send({ type: "stop" });
@@ -1695,8 +1884,8 @@ function createSession({
1695
1884
  if (proc) {
1696
1885
  await Promise.race([
1697
1886
  proc.wait(),
1698
- new Promise((resolve) => {
1699
- stopTimer = setTimeout(resolve, 5e3);
1887
+ new Promise((resolve2) => {
1888
+ stopTimer = setTimeout(resolve2, 5e3);
1700
1889
  stopTimer.unref?.();
1701
1890
  })
1702
1891
  ]);