@rynfar/meridian 1.40.0 → 1.41.0

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.
@@ -1,7 +1,9 @@
1
1
  // src/proxy/tokenRefresh.ts
2
2
  import { execFile as execFileCb } from "child_process";
3
- import { existsSync, readFileSync, writeFileSync } from "fs";
3
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
4
4
  import { homedir, platform, userInfo } from "os";
5
+ import { join, dirname, resolve } from "path";
6
+ import { createHash } from "crypto";
5
7
  import { promisify } from "util";
6
8
 
7
9
  // src/logger.ts
@@ -72,6 +74,17 @@ var OAUTH_TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
72
74
  var OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
73
75
  var KEYCHAIN_SERVICE = "Claude Code-credentials";
74
76
  var CREDENTIALS_FILE = `${homedir()}/.claude/.credentials.json`;
77
+ var DEFAULT_CLAUDE_DIR = `${homedir()}/.claude`;
78
+ function configDirToKeychainService(claudeConfigDir) {
79
+ const abs = resolve(claudeConfigDir);
80
+ if (abs === resolve(DEFAULT_CLAUDE_DIR))
81
+ return KEYCHAIN_SERVICE;
82
+ const hash = createHash("sha256").update(abs).digest("hex").slice(0, 8);
83
+ return `${KEYCHAIN_SERVICE}-${hash}`;
84
+ }
85
+ function configDirToCredentialsFile(claudeConfigDir) {
86
+ return join(resolve(claudeConfigDir), ".credentials.json");
87
+ }
75
88
  function parseKeychainValue(raw) {
76
89
  const trimmed = raw.trim();
77
90
  try {
@@ -83,57 +96,74 @@ function parseKeychainValue(raw) {
83
96
  } catch {}
84
97
  return null;
85
98
  }
86
- var keychainWasHex = false;
87
- var macosStore = {
88
- async read() {
89
- try {
90
- const { stdout } = await execFile("/usr/bin/security", ["find-generic-password", "-s", KEYCHAIN_SERVICE, "-a", userInfo().username, "-w"], { timeout: 5000 });
91
- const parsed = parseKeychainValue(stdout);
92
- if (!parsed)
93
- throw new Error("Could not parse keychain value as JSON or hex-encoded JSON");
94
- keychainWasHex = parsed.wasHex;
95
- return parsed.credentials;
96
- } catch (err) {
97
- claudeLog("token_refresh.keychain_read_failed", { error: String(err) });
98
- return null;
99
- }
100
- },
101
- async write(credentials) {
102
- const json = JSON.stringify(credentials, null, 2);
103
- const value = keychainWasHex ? Buffer.from(json).toString("hex") : json;
104
- try {
105
- await execFile("/usr/bin/security", ["add-generic-password", "-U", "-s", KEYCHAIN_SERVICE, "-a", userInfo().username, "-w", value], { timeout: 5000 });
106
- return true;
107
- } catch (err) {
108
- claudeLog("token_refresh.keychain_write_failed", { error: String(err) });
109
- return false;
99
+ var keychainWasHexByService = new Map;
100
+ function buildMacosStore(serviceName) {
101
+ return {
102
+ async read() {
103
+ try {
104
+ const { stdout } = await execFile("/usr/bin/security", ["find-generic-password", "-s", serviceName, "-a", userInfo().username, "-w"], { timeout: 5000 });
105
+ const parsed = parseKeychainValue(stdout);
106
+ if (!parsed)
107
+ throw new Error("Could not parse keychain value as JSON or hex-encoded JSON");
108
+ keychainWasHexByService.set(serviceName, parsed.wasHex);
109
+ return parsed.credentials;
110
+ } catch (err) {
111
+ claudeLog("token_refresh.keychain_read_failed", { service: serviceName, error: String(err) });
112
+ return null;
113
+ }
114
+ },
115
+ async write(credentials) {
116
+ const json = JSON.stringify(credentials, null, 2);
117
+ const wasHex = keychainWasHexByService.get(serviceName) ?? false;
118
+ const value = wasHex ? Buffer.from(json).toString("hex") : json;
119
+ try {
120
+ await execFile("/usr/bin/security", ["add-generic-password", "-U", "-s", serviceName, "-a", userInfo().username, "-w", value], { timeout: 5000 });
121
+ return true;
122
+ } catch (err) {
123
+ claudeLog("token_refresh.keychain_write_failed", { service: serviceName, error: String(err) });
124
+ return false;
125
+ }
110
126
  }
111
- }
112
- };
113
- var fileStore = {
114
- async read() {
115
- try {
116
- if (!existsSync(CREDENTIALS_FILE))
127
+ };
128
+ }
129
+ var macosStore = buildMacosStore(KEYCHAIN_SERVICE);
130
+ function buildFileStore(filePath) {
131
+ return {
132
+ async read() {
133
+ try {
134
+ if (!existsSync(filePath))
135
+ return null;
136
+ return JSON.parse(readFileSync(filePath, "utf-8"));
137
+ } catch (err) {
138
+ claudeLog("token_refresh.file_read_failed", { path: filePath, error: String(err) });
117
139
  return null;
118
- return JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
119
- } catch (err) {
120
- claudeLog("token_refresh.file_read_failed", { error: String(err) });
121
- return null;
140
+ }
141
+ },
142
+ async write(credentials) {
143
+ try {
144
+ mkdirSync(dirname(filePath), { recursive: true });
145
+ writeFileSync(filePath, JSON.stringify(credentials, null, 2), "utf-8");
146
+ return true;
147
+ } catch (err) {
148
+ claudeLog("token_refresh.file_write_failed", { path: filePath, error: String(err) });
149
+ return false;
150
+ }
122
151
  }
123
- },
124
- async write(credentials) {
125
- try {
126
- writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), "utf-8");
127
- return true;
128
- } catch (err) {
129
- claudeLog("token_refresh.file_write_failed", { error: String(err) });
130
- return false;
152
+ };
153
+ }
154
+ var fileStore = buildFileStore(CREDENTIALS_FILE);
155
+ function createPlatformCredentialStore(opts) {
156
+ if (opts?.claudeConfigDir) {
157
+ if (platform() === "darwin") {
158
+ return buildMacosStore(configDirToKeychainService(opts.claudeConfigDir));
131
159
  }
160
+ return buildFileStore(configDirToCredentialsFile(opts.claudeConfigDir));
132
161
  }
133
- };
134
- function createPlatformCredentialStore() {
135
162
  return platform() === "darwin" ? macosStore : fileStore;
136
163
  }
164
+ function credentialsFilePathForProfile(claudeConfigDir) {
165
+ return claudeConfigDir ? configDirToCredentialsFile(claudeConfigDir) : CREDENTIALS_FILE;
166
+ }
137
167
  var inflightRefresh = null;
138
168
  async function refreshOAuthToken(store) {
139
169
  if (inflightRefresh)
@@ -200,4 +230,4 @@ function resetInflightRefresh() {
200
230
  inflightRefresh = null;
201
231
  }
202
232
 
203
- export { withClaudeLogContext, claudeLog, createPlatformCredentialStore, refreshOAuthToken, resetInflightRefresh };
233
+ export { withClaudeLogContext, claudeLog, configDirToKeychainService, configDirToCredentialsFile, createPlatformCredentialStore, credentialsFilePathForProfile, refreshOAuthToken, resetInflightRefresh };