@swttch/extend-kit 0.4.0 → 0.6.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.
package/README.ko.md CHANGED
@@ -29,6 +29,23 @@ npm install @swttch/extend-kit
29
29
 
30
30
  Node.js 버전 20 이상이 필요합니다.
31
31
 
32
+ ## 프록시 지원
33
+
34
+ 프록시를 거쳐야 인터넷에 나가는 환경이라면, API 요청과 받아쓰기 모두 그 프록시를 지납니다. 읽는 환경변수는 curl과 `claude` CLI가 이미 읽는 것과 같으므로, 그 도구들이 동작하는 머신이라면 따로 설정할 것이 없습니다.
35
+
36
+ | 환경변수 | 적용 대상 |
37
+ |----------|-----------|
38
+ | `HTTPS_PROXY` / `https_proxy` | `https://` 와 `wss://` 연결 |
39
+ | `HTTP_PROXY` / `http_proxy` | `http://` 와 `ws://` 연결 |
40
+ | `ALL_PROXY` / `all_proxy` | 위 둘이 없을 때 사용 |
41
+ | `NO_PROXY` / `no_proxy` | 프록시를 거치지 않을 호스트. `*` 는 프록시를 통째로 끕니다 |
42
+
43
+ 프록시 URL은 `http://`, `https://`, `socks://`, `socks4://`, `socks4a://`, `socks5://`, `socks5h://` 를 지원합니다. 따라서 사내 프록시뿐 아니라 `ssh -D` 로 연 터널도 그대로 동작합니다. URL에 담긴 자격증명(`http://사용자:비밀번호@호스트:포트`)은 프록시 인증에 사용됩니다.
44
+
45
+ `NO_PROXY` 는 통용되는 규칙을 따릅니다. 쉼표로 구분하고, 맨 앞의 점은 무시하며, 도메인만 적으면 그 호스트와 하위 도메인이 모두 해당됩니다. 뒤에 `:포트` 를 붙이면 그 포트에만 적용됩니다.
46
+
47
+ 프록시가 아니라 게이트웨이를 통해 API에 접속한다면 `ANTHROPIC_BASE_URL` 을 설정하십시오. `claude` CLI가 읽는 환경변수와 같습니다.
48
+
32
49
  ## 빠른 시작
33
50
 
34
51
  ### SDK 사용
@@ -129,7 +146,7 @@ if (isTokenExpired(credentials)) {
129
146
 
130
147
  API 호출을 위한 클라이언트 클래스입니다. 서브모듈을 통해 API 엔드포인트에 접근합니다.
131
148
 
132
- #### `constructor(auth?: string | { apiKey: string })`
149
+ #### `constructor(auth?: string | { apiKey: string }, options?: ClaudeCodeClientOptions)`
133
150
 
134
151
  OAuth 접근 토큰이나 API Key로 클라이언트를 초기화합니다. 인수를 전달하지 않으면 자동으로 크레덴셜을 읽어옵니다.
135
152
 
@@ -142,8 +159,17 @@ const client = new ClaudeCodeClient(myOAuthAccessToken);
142
159
 
143
160
  // API Key
144
161
  const client = new ClaudeCodeClient({ apiKey: process.env.ANTHROPIC_API_KEY });
162
+
163
+ // API에 직접 붙지 않고 게이트웨이를 거치는 경우
164
+ const client = new ClaudeCodeClient(myOAuthAccessToken, { baseUrl: 'https://gateway.internal' });
145
165
  ```
146
166
 
167
+ | 옵션 | 기본값 | 설명 |
168
+ |------|--------|------|
169
+ | `baseUrl` | `ANTHROPIC_BASE_URL`, 없으면 `https://api.anthropic.com` | 요청을 보낼 API 주소 |
170
+
171
+ 프록시 설정은 환경변수에서 읽으므로 여기에 옵션으로 넘길 필요가 없습니다. [프록시 지원](#프록시-지원)을 참고하십시오.
172
+
147
173
  #### `oauth: OAuthApi`
148
174
 
149
175
  OAuth 관련 API를 제공하는 서브모듈입니다.
@@ -214,6 +240,8 @@ try {
214
240
  | `token_expired` | OAuth 토큰이 만료됨 | `claude login` 명령 실행으로 토큰 갱신 |
215
241
  | `api_error` | API 요청 실패 (HTTP 상태 포함) | — |
216
242
  | `unsupported_platform` | 지원하지 않는 플랫폼 | — |
243
+ | `network_error` | API에 접속하지 못함 | 프록시를 거친 경우 그 사실을 알려줍니다 |
244
+ | `invalid_proxy` | 프록시 환경변수가 URL이 아니거나 지원하지 않는 스킴 | 기대하는 형식을 알려줍니다 |
217
245
 
218
246
  ## 타입 정의
219
247
 
package/README.md CHANGED
@@ -29,6 +29,31 @@ npm install @swttch/extend-kit
29
29
 
30
30
  Node.js version 20 or higher is required.
31
31
 
32
+ ## Proxy Support
33
+
34
+ If your machine reaches the internet through a proxy, both the API requests and
35
+ dictation go through it. The variables are the ones curl and the `claude` CLI
36
+ already read, so a machine set up for those needs nothing extra here.
37
+
38
+ | Variable | Applies to |
39
+ | --- | --- |
40
+ | `HTTPS_PROXY` / `https_proxy` | `https://` and `wss://` connections |
41
+ | `HTTP_PROXY` / `http_proxy` | `http://` and `ws://` connections |
42
+ | `ALL_PROXY` / `all_proxy` | Used when neither of the above is set |
43
+ | `NO_PROXY` / `no_proxy` | Hosts to connect to directly. `*` disables the proxy entirely |
44
+
45
+ `http://`, `https://`, `socks://`, `socks4://`, `socks4a://`, `socks5://` and
46
+ `socks5h://` proxy URLs are supported, so an `ssh -D` tunnel works as well as a
47
+ corporate egress proxy. Credentials embedded in the URL
48
+ (`http://user:pass@host:port`) are used for proxy authentication.
49
+
50
+ `NO_PROXY` follows the usual conventions: a comma-separated list, a leading dot
51
+ ignored, a bare entry matching that host and its subdomains, and an optional
52
+ `:port` to limit the entry to one port.
53
+
54
+ If you talk to the API through a gateway rather than a proxy, set
55
+ `ANTHROPIC_BASE_URL` — the same variable the `claude` CLI reads.
56
+
32
57
  ## Quick Start
33
58
 
34
59
  ### SDK Usage
@@ -129,7 +154,7 @@ if (isTokenExpired(credentials)) {
129
154
 
130
155
  A client class for making API calls. Access API endpoints through sub-modules.
131
156
 
132
- #### `constructor(auth?: string | { apiKey: string })`
157
+ #### `constructor(auth?: string | { apiKey: string }, options?: ClaudeCodeClientOptions)`
133
158
 
134
159
  Initializes the client with OAuth access token or API Key. If no argument is provided, credentials are resolved automatically.
135
160
 
@@ -142,8 +167,17 @@ const client = new ClaudeCodeClient(myAccessToken);
142
167
 
143
168
  // API Key
144
169
  const client = new ClaudeCodeClient({ apiKey: process.env.ANTHROPIC_API_KEY });
170
+
171
+ // Talk to a gateway instead of the API directly
172
+ const client = new ClaudeCodeClient(myAccessToken, { baseUrl: 'https://gateway.internal' });
145
173
  ```
146
174
 
175
+ | Option | Default | Description |
176
+ | --- | --- | --- |
177
+ | `baseUrl` | `ANTHROPIC_BASE_URL`, else `https://api.anthropic.com` | API origin to send requests to |
178
+
179
+ Proxy settings are read from the environment and need no option here — see [Proxy Support](#proxy-support).
180
+
147
181
  #### `oauth: OAuthApi`
148
182
 
149
183
  A sub-module that provides OAuth-related API methods.
@@ -214,6 +248,8 @@ try {
214
248
  | `token_expired` | OAuth token has expired | Run `claude login` to refresh your token |
215
249
  | `api_error` | API request failed (includes HTTP status) | — |
216
250
  | `unsupported_platform` | Platform not supported | — |
251
+ | `network_error` | The API could not be reached | Names the proxy when one was in use |
252
+ | `invalid_proxy` | A proxy variable is not a URL, or its scheme is not supported | Shows the expected form |
217
253
 
218
254
  ## Type Definitions
219
255
 
@@ -504,3 +540,16 @@ MIT
504
540
  ## Author
505
541
 
506
542
  yhk1038
543
+
544
+ ## Saved-account usage (CLI)
545
+
546
+ `ccb --capabilities --json` reports `oauth.usage.account-file` when supported.
547
+ `ccb oauth usage --account-file="/path/to/account-snapshot.json" --json` reads a
548
+ CCG account snapshot (`credentials` is a JSON string, `oauthAccount.emailAddress`
549
+ identifies the account) and queries that account without switching the live login.
550
+ Credential values are never command arguments or CLI output. The CLI may use a
551
+ newer live credential when the live email matches the selected snapshot; otherwise
552
+ it uses the snapshot. Expired tokens return `token_expired`. No token refresh,
553
+ credential writes, caching, retries or automatic account switching are performed.
554
+ The caller owns process timeout and recovery policy. Plain `ccb oauth usage`
555
+ retains its existing behaviour.
@@ -1,10 +1,19 @@
1
1
  import { OAuthApi } from './oauth.js';
2
2
  import type { ClientAuth } from '../auth/index.js';
3
+ /** Options that change where or how requests go out. */
4
+ export interface ClaudeCodeClientOptions {
5
+ /**
6
+ * API origin to talk to. Defaults to ANTHROPIC_BASE_URL when set, and to the
7
+ * real API otherwise.
8
+ */
9
+ baseUrl?: string;
10
+ }
3
11
  export declare class ClaudeCodeClient {
4
12
  private auth?;
5
13
  private accessToken?;
6
14
  private _oauth?;
7
- constructor(auth?: ClientAuth);
15
+ private readonly baseUrl;
16
+ constructor(auth?: ClientAuth, options?: ClaudeCodeClientOptions);
8
17
  get isApiKeyMode(): boolean;
9
18
  get oauth(): OAuthApi;
10
19
  private resolveToken;
@@ -1,13 +1,30 @@
1
1
  import { OAuthApi } from './oauth.js';
2
+ import { getJson } from './http.js';
2
3
  import { getCredentials, getAccessToken, isApiKeyAuth } from '../auth/index.js';
3
4
  import { CcbError } from '../errors.js';
4
5
  const BASE_URL = 'https://api.anthropic.com';
6
+ /**
7
+ * The origin to use, with any trailing slash removed so that joining a path that
8
+ * starts with "/" cannot produce a double slash.
9
+ *
10
+ * ANTHROPIC_BASE_URL is honored because the `claude` CLI honors it: a user
11
+ * behind a corporate gateway points both at the same place, and `ccb` claiming
12
+ * not to know the setting the rest of their toolchain uses would be a gap for no
13
+ * reason. It also lets the tests here talk to a local server over the real
14
+ * request path instead of stubbing the transport.
15
+ */
16
+ function resolveBaseUrl(explicit) {
17
+ const chosen = explicit ?? process.env.ANTHROPIC_BASE_URL?.trim() ?? '';
18
+ return (chosen || BASE_URL).replace(/\/+$/, '');
19
+ }
5
20
  export class ClaudeCodeClient {
6
21
  auth;
7
22
  accessToken;
8
23
  _oauth;
9
- constructor(auth) {
24
+ baseUrl;
25
+ constructor(auth, options = {}) {
10
26
  this.auth = auth;
27
+ this.baseUrl = resolveBaseUrl(options.baseUrl);
11
28
  if (typeof auth === 'string') {
12
29
  this.accessToken = auth;
13
30
  }
@@ -41,14 +58,14 @@ export class ClaudeCodeClient {
41
58
  ...headers,
42
59
  };
43
60
  }
44
- const response = await fetch(`${BASE_URL}${path}`, {
45
- method: 'GET',
46
- headers: requestHeaders,
47
- });
61
+ // getJson rather than fetch: fetch cannot be routed through a proxy, and on a
62
+ // machine that only reaches the internet through one, every call here fails.
63
+ // See src/proxy.ts.
64
+ const response = await getJson(`${this.baseUrl}${path}`, requestHeaders);
48
65
  if (!response.ok) {
49
66
  throw new CcbError(`API error ${response.status}: ${response.statusText}`, 'api_error');
50
67
  }
51
- return response.json();
68
+ return response.body;
52
69
  }
53
70
  }
54
71
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/battery/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,MAAM,QAAQ,GAAG,2BAA2B,CAAC;AAE7C,MAAM,OAAO,gBAAgB;IACnB,IAAI,CAAc;IAClB,WAAW,CAAU;IACrB,MAAM,CAAY;IAE1B,YAAY,IAAiB;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,IAAI,YAAY;QACd,OAAO,YAAY,CAAC,IAAI,CAAC,IAAkB,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,KAAK,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC;IAEO,KAAK,CAAC,YAAY;QACxB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,WAAW,GAAG,MAAM,cAAc,EAAE,CAAC;YAC3C,IAAI,CAAC,WAAW,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAI,IAAY,EAAE,OAAgC;QAC9D,IAAI,cAAsC,CAAC;QAE3C,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACvD,cAAc,GAAG;gBACf,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM;gBAC7B,mBAAmB,EAAE,YAAY;gBACjC,GAAG,OAAO;aACX,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;YACxC,cAAc,GAAG;gBACf,aAAa,EAAE,UAAU,KAAK,EAAE;gBAChC,GAAG,OAAO;aACX,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,GAAG,IAAI,EAAE,EAAE;YACjD,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,cAAc;SACxB,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,QAAQ,CAAC,aAAa,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,UAAU,EAAE,EAAE,WAAW,CAAC,CAAC;QAC1F,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,EAAgB,CAAC;IACvC,CAAC;CACF"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/battery/api/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,MAAM,QAAQ,GAAG,2BAA2B,CAAC;AAW7C;;;;;;;;;GASG;AACH,SAAS,cAAc,CAAC,QAA4B;IAClD,MAAM,MAAM,GAAG,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxE,OAAO,CAAC,MAAM,IAAI,QAAQ,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,OAAO,gBAAgB;IACnB,IAAI,CAAc;IAClB,WAAW,CAAU;IACrB,MAAM,CAAY;IACT,OAAO,CAAS;IAEjC,YAAY,IAAiB,EAAE,UAAmC,EAAE;QAClE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,IAAI,YAAY;QACd,OAAO,YAAY,CAAC,IAAI,CAAC,IAAkB,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,KAAK,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC;IAEO,KAAK,CAAC,YAAY;QACxB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,WAAW,GAAG,MAAM,cAAc,EAAE,CAAC;YAC3C,IAAI,CAAC,WAAW,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,QAAQ,CAAI,IAAY,EAAE,OAAgC;QAC9D,IAAI,cAAsC,CAAC;QAE3C,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACvD,cAAc,GAAG;gBACf,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM;gBAC7B,mBAAmB,EAAE,YAAY;gBACjC,GAAG,OAAO;aACX,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,YAAY,EAAE,CAAC;YACxC,cAAc,GAAG;gBACf,aAAa,EAAE,UAAU,KAAK,EAAE;gBAChC,GAAG,OAAO;aACX,CAAC;QACJ,CAAC;QAED,8EAA8E;QAC9E,6EAA6E;QAC7E,oBAAoB;QACpB,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAI,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE,cAAc,CAAC,CAAC;QAE5E,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,QAAQ,CAAC,aAAa,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,UAAU,EAAE,EAAE,WAAW,CAAC,CAAC;QAC1F,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;CACF"}
@@ -0,0 +1,17 @@
1
+ import { type Agent } from 'node:http';
2
+ /** What a caller needs to know about the response, mirroring the shape `fetch` returns. */
3
+ export interface JsonResponse<T> {
4
+ ok: boolean;
5
+ status: number;
6
+ statusText: string;
7
+ body: T;
8
+ }
9
+ /**
10
+ * GET a JSON document, through a proxy when one is configured.
11
+ *
12
+ * A dead or misconfigured proxy fails at connect time, and the raw errno that
13
+ * surfaces ("ECONNREFUSED 127.0.0.1:8080") names the proxy without saying that a
14
+ * proxy is what failed. Since the proxy is invisible in the URL the user asked
15
+ * for, the error says so explicitly.
16
+ */
17
+ export declare function getJson<T>(url: string, headers: Record<string, string>, agentOverride?: Agent): Promise<JsonResponse<T>>;
@@ -0,0 +1,61 @@
1
+ import { request as httpsRequest } from 'node:https';
2
+ import { request as httpRequest } from 'node:http';
3
+ import { proxyAgentFor } from '../../proxy.js';
4
+ import { CcbError } from '../errors.js';
5
+ /**
6
+ * Global `fetch` cannot be pointed at a proxy — it ignores the proxy environment
7
+ * variables and takes no `agent` — so it is unusable here. `node:https` accepts
8
+ * the very agent `ws` accepts, which lets one proxy implementation serve both
9
+ * transports. See {@link proxyAgentFor}.
10
+ */
11
+ const SOCKET_TIMEOUT_MS = 60_000;
12
+ /**
13
+ * GET a JSON document, through a proxy when one is configured.
14
+ *
15
+ * A dead or misconfigured proxy fails at connect time, and the raw errno that
16
+ * surfaces ("ECONNREFUSED 127.0.0.1:8080") names the proxy without saying that a
17
+ * proxy is what failed. Since the proxy is invisible in the URL the user asked
18
+ * for, the error says so explicitly.
19
+ */
20
+ export async function getJson(url, headers, agentOverride) {
21
+ const agent = agentOverride ?? proxyAgentFor(url);
22
+ const viaProxy = agent !== undefined;
23
+ // The API is always https; plain http is here so tests can run against a local
24
+ // server without a certificate, and it costs one branch.
25
+ const request = new URL(url).protocol === 'http:' ? httpRequest : httpsRequest;
26
+ const { status, statusText, text } = await new Promise((resolve, reject) => {
27
+ const req = request(url, { method: 'GET', headers, agent }, (res) => {
28
+ const chunks = [];
29
+ res.on('data', (chunk) => chunks.push(chunk));
30
+ res.on('end', () => resolve({
31
+ status: res.statusCode ?? 0,
32
+ statusText: res.statusMessage ?? '',
33
+ text: Buffer.concat(chunks).toString('utf-8'),
34
+ }));
35
+ res.on('error', reject);
36
+ });
37
+ // `https.request` waits forever by default; a proxy that accepts the socket
38
+ // and then goes quiet would hang the CLI with no output at all.
39
+ req.setTimeout(SOCKET_TIMEOUT_MS, () => {
40
+ req.destroy(new CcbError(`Request to ${url} timed out after ${SOCKET_TIMEOUT_MS / 1000}s`, 'network_error', viaProxy ? 'The configured proxy accepted the connection but never replied.' : undefined));
41
+ });
42
+ req.on('error', (err) => {
43
+ if (err instanceof CcbError)
44
+ return reject(err);
45
+ reject(new CcbError(`Could not reach ${new URL(url).host}: ${err.message}`, 'network_error', viaProxy
46
+ ? 'This connection went through the proxy from HTTP_PROXY/HTTPS_PROXY/ALL_PROXY. Check that the proxy is running and reachable.'
47
+ : undefined));
48
+ });
49
+ req.end();
50
+ });
51
+ if (status < 200 || status >= 300) {
52
+ return { ok: false, status, statusText, body: undefined };
53
+ }
54
+ try {
55
+ return { ok: true, status, statusText, body: JSON.parse(text) };
56
+ }
57
+ catch {
58
+ throw new CcbError(`Expected JSON from ${url} but got something else`, 'api_error', viaProxy ? 'A proxy that returns an error page instead of the response looks like this.' : undefined);
59
+ }
60
+ }
61
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/battery/api/http.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
1
  export { ClaudeCodeClient } from './client.js';
2
+ export type { ClaudeCodeClientOptions } from './client.js';
2
3
  export { OAuthApi } from './oauth.js';
3
4
  export type { UsageResponse, UsageBucket, ExtraUsage, ProfileResponse, AccountInfo, OrganizationInfo, ApplicationInfo, } from './types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/battery/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/battery/api/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
@@ -1,2 +1,2 @@
1
1
  import type { ClaudeCredentials } from './types.js';
2
- export declare function readKeychainCredentials(): Promise<ClaudeCredentials>;
2
+ export declare function readKeychainCredentials(configDir?: string): Promise<ClaudeCredentials>;
@@ -1,4 +1,5 @@
1
1
  import { execFile } from 'node:child_process';
2
+ import { createHash } from 'node:crypto';
2
3
  import { promisify } from 'node:util';
3
4
  const execFileAsync = promisify(execFile);
4
5
  const SERVICE_NAME = 'Claude Code-credentials';
@@ -6,11 +7,11 @@ async function getOsUsername() {
6
7
  const { stdout } = await execFileAsync('whoami');
7
8
  return stdout.trim();
8
9
  }
9
- export async function readKeychainCredentials() {
10
+ export async function readKeychainCredentials(configDir) {
10
11
  const account = await getOsUsername();
11
12
  const { stdout } = await execFileAsync('security', [
12
13
  'find-generic-password',
13
- '-s', SERVICE_NAME,
14
+ '-s', configDir ? `${SERVICE_NAME}-${createHash('sha256').update(configDir).digest('hex').slice(0, 8)}` : SERVICE_NAME,
14
15
  '-a', account,
15
16
  '-w',
16
17
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../../src/battery/auth/keychain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,MAAM,YAAY,GAAG,yBAAyB,CAAC;AAE/C,KAAK,UAAU,aAAa;IAC1B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;IACjD,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAC3C,MAAM,OAAO,GAAG,MAAM,aAAa,EAAE,CAAC;IACtC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE;QACjD,uBAAuB;QACvB,IAAI,EAAE,YAAY;QAClB,IAAI,EAAE,OAAO;QACb,IAAI;KACL,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAsB,CAAC;IACpD,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"keychain.js","sourceRoot":"","sources":["../../../src/battery/auth/keychain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,MAAM,YAAY,GAAG,yBAAyB,CAAC;AAE/C,KAAK,UAAU,aAAa;IAC1B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;IACjD,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,SAAkB;IAC9D,MAAM,OAAO,GAAG,MAAM,aAAa,EAAE,CAAC;IACtC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE;QACjD,uBAAuB;QACvB,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,YAAY,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY;QACtH,IAAI,EAAE,OAAO;QACb,IAAI;KACL,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAsB,CAAC;IACpD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ClaudeCredentials } from '../battery/auth/types.js';
2
+ /** Read a CCG saved-account snapshot without replacing the active credential slot. */
3
+ export declare function getAccountCredentials(file: string): Promise<ClaudeCredentials>;
@@ -0,0 +1,43 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { getCredentials } from '../battery/auth/index.js';
5
+ import { readKeychainCredentials } from '../battery/auth/keychain.js';
6
+ import { CcbError } from '../battery/errors.js';
7
+ /** Read a CCG saved-account snapshot without replacing the active credential slot. */
8
+ export async function getAccountCredentials(file) {
9
+ let snapshot;
10
+ let credentials;
11
+ try {
12
+ snapshot = JSON.parse(await readFile(file, 'utf8'));
13
+ credentials = JSON.parse(snapshot.credentials ?? '');
14
+ if (!credentials.claudeAiOauth?.accessToken)
15
+ throw new Error('Missing access token');
16
+ }
17
+ catch {
18
+ // Parsing errors must never echo a credential blob in stderr.
19
+ throw new CcbError('Saved account credentials are unavailable or invalid.', 'credentials_not_found');
20
+ }
21
+ const email = snapshot.oauthAccount?.emailAddress;
22
+ const configDir = process.env.CLAUDE_CONFIG_DIR;
23
+ const liveEmail = async () => {
24
+ const metadata = JSON.parse(await readFile(join(configDir ?? homedir(), '.claude.json'), 'utf8'));
25
+ return metadata.oauthAccount?.emailAddress;
26
+ };
27
+ // A currently active account can have a newer token than its saved snapshot.
28
+ // Never use the live slot for a different account. No writes or token refresh.
29
+ try {
30
+ if (email && await liveEmail() === email) {
31
+ const live = process.platform === 'darwin'
32
+ ? await readKeychainCredentials(configDir) : await getCredentials();
33
+ if (await liveEmail() === email && live.claudeAiOauth?.accessToken)
34
+ credentials = live;
35
+ }
36
+ }
37
+ catch { /* The saved snapshot remains the fallback. */ }
38
+ if (Number.isFinite(credentials.claudeAiOauth.expiresAt) && Date.now() >= credentials.claudeAiOauth.expiresAt) {
39
+ throw new CcbError('Saved account token has expired. Log in to this account again.', 'token_expired');
40
+ }
41
+ return credentials;
42
+ }
43
+ //# sourceMappingURL=account-credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account-credentials.js","sourceRoot":"","sources":["../../src/cli/account-credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAEtE,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,sFAAsF;AACtF,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,IAAY;IACtD,IAAI,QAA4E,CAAC;IACjF,IAAI,WAA8B,CAAC;IACnC,IAAI,CAAC;QACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QACpD,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,WAAW,CAAC,aAAa,EAAE,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IACvF,CAAC;IAAC,MAAM,CAAC;QACP,8DAA8D;QAC9D,MAAM,IAAI,QAAQ,CAAC,uDAAuD,EAAE,uBAAuB,CAAC,CAAC;IACvG,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IAClD,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAChD,MAAM,SAAS,GAAG,KAAK,IAAiC,EAAE;QACxD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,SAAS,IAAI,OAAO,EAAE,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAE/F,CAAC;QACF,OAAO,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IAC7C,CAAC,CAAC;IACF,6EAA6E;IAC7E,+EAA+E;IAC/E,IAAI,CAAC;QACH,IAAI,KAAK,IAAI,MAAM,SAAS,EAAE,KAAK,KAAK,EAAE,CAAC;YACzC,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,KAAK,QAAQ;gBACxC,CAAC,CAAC,MAAM,uBAAuB,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,cAAc,EAAE,CAAC;YACtE,IAAI,MAAM,SAAS,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC,aAAa,EAAE,WAAW;gBAAE,WAAW,GAAG,IAAI,CAAC;QACzF,CAAC;IACH,CAAC;IAAC,MAAM,CAAC,CAAC,8CAA8C,CAAC,CAAC;IAC1D,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,aAAa,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,WAAW,CAAC,aAAa,CAAC,SAAS,EAAE,CAAC;QAC9G,MAAM,IAAI,QAAQ,CAAC,gEAAgE,EAAE,eAAe,CAAC,CAAC;IACxG,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC"}
package/dist/cli/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { getCredentials, getAccessToken } from '../battery/auth/index.js';
3
3
  import { ClaudeCodeClient } from '../battery/api/index.js';
4
4
  import { oauthCommand } from './oauth.js';
5
+ import { getAccountCredentials } from './account-credentials.js';
5
6
  import { CcbError } from '../battery/errors.js';
6
7
  import { readFileSync } from 'node:fs';
7
8
  import { fileURLToPath } from 'node:url';
@@ -13,13 +14,21 @@ const args = process.argv.slice(2);
13
14
  const command = args.filter((a) => !a.startsWith('-'));
14
15
  const flags = new Set(args.filter((a) => a.startsWith('-')));
15
16
  const jsonOutput = flags.has('--json');
17
+ const accountFile = args.find(arg => arg.startsWith('--account-file='))?.slice('--account-file='.length);
16
18
  async function createClient() {
17
- const credentials = await getCredentials();
19
+ const credentials = accountFile ? await getAccountCredentials(accountFile) : await getCredentials();
18
20
  const token = getAccessToken(credentials);
19
21
  return new ClaudeCodeClient(token);
20
22
  }
21
23
  async function run() {
22
24
  const [module, ...subcommand] = command;
25
+ if (flags.has('--capabilities')) {
26
+ console.log(JSON.stringify({ capabilities: ['oauth.usage.account-file'] }));
27
+ return;
28
+ }
29
+ if (args.some(arg => arg.startsWith('--account-file')) && (!accountFile || module !== 'oauth' || subcommand[0] !== 'usage')) {
30
+ throw new CcbError('Use oauth usage --account-file=<snapshot path>.', 'invalid_argument');
31
+ }
23
32
  if (flags.has('-v') || flags.has('--version')) {
24
33
  console.log(VERSION);
25
34
  return;
@@ -34,6 +43,8 @@ Commands:
34
43
  oauth profile Show account profile
35
44
 
36
45
  Options:
46
+ --account-file=<path> Read usage for a CCG saved-account snapshot (no account switch)
47
+ --capabilities Output supported CLI capabilities as JSON
37
48
  --json Output as JSON
38
49
  -h, --help Show help
39
50
  -v, --version Show version`);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { Agent } from 'node:http';
2
+ /**
3
+ * Whether NO_PROXY exempts this host, following the de-facto rules curl uses:
4
+ * a comma-separated list, `*` meaning everything, a leading dot ignored, and a
5
+ * bare entry matching that host plus its subdomains. An entry may pin a port,
6
+ * in which case the port must match too.
7
+ */
8
+ export declare function isProxyBypassed(host: string, port: string, noProxy: string | undefined): boolean;
9
+ /**
10
+ * The proxy URL that applies to `targetUrl`, or undefined for a direct connection.
11
+ *
12
+ * `wss:`/`ws:` are resolved as their HTTP equivalents because that is how the
13
+ * connection is actually made — a WebSocket starts life as an HTTP upgrade, so
14
+ * a user who set HTTPS_PROXY expects `wss://` to go through it.
15
+ */
16
+ export declare function resolveProxyUrl(targetUrl: string, env?: NodeJS.ProcessEnv): string | undefined;
17
+ /**
18
+ * An agent that routes a connection to `targetUrl` through the configured proxy,
19
+ * or undefined when no proxy applies and the connection should go out directly.
20
+ *
21
+ * The result is an `http.Agent`, which is the one shape both callers accept:
22
+ * `https.request` takes it as `agent`, and `ws` takes the same object under the
23
+ * same name. That is why this package talks to the API over `node:https` rather
24
+ * than global `fetch` — `fetch` cannot be handed one of these, so using it would
25
+ * mean two separate proxy implementations for the two transports.
26
+ *
27
+ * A malformed proxy URL throws rather than falling back to a direct connection.
28
+ * On a machine that can only reach the internet through a proxy the direct
29
+ * attempt fails anyway, and it fails with a connection error that says nothing
30
+ * about the real cause; naming the bad setting is far more useful.
31
+ */
32
+ export declare function proxyAgentFor(targetUrl: string, env?: NodeJS.ProcessEnv): Agent | undefined;
package/dist/proxy.js ADDED
@@ -0,0 +1,121 @@
1
+ import { HttpsProxyAgent } from 'https-proxy-agent';
2
+ import { SocksProxyAgent } from 'socks-proxy-agent';
3
+ import { CcbError } from './battery/errors.js';
4
+ /**
5
+ * Proxy support for every outbound connection this package makes.
6
+ *
7
+ * Node does not route outbound requests through a proxy on its own. Its global
8
+ * `fetch` ignores HTTP_PROXY entirely (Node 24 added an opt-in flag for it, but
9
+ * this package supports Node 20+, and that flag does not cover SOCKS at all), and
10
+ * `ws` only proxies when handed an agent. So a user whose machine reaches the
11
+ * internet only through a proxy — a corporate egress proxy, or an `ssh -D`
12
+ * tunnel — got a plain connection failure from `ccb`, while `claude` itself
13
+ * worked because the CLI does its own proxying. That gap is what this module
14
+ * closes: see Swttch/swttch#181.
15
+ *
16
+ * The variables read here are the ones curl and the claude CLI already read, so
17
+ * a machine that is set up for those needs no extra configuration for `ccb`.
18
+ */
19
+ /** Proxy URL schemes that mean SOCKS rather than an HTTP CONNECT proxy. */
20
+ const SOCKS_PROTOCOLS = new Set(['socks:', 'socks4:', 'socks4a:', 'socks5:', 'socks5h:']);
21
+ /**
22
+ * Read a proxy variable in either spelling.
23
+ *
24
+ * Both cases are in real use — most tooling documents the uppercase form, while
25
+ * curl has historically honored the lowercase one — and users copy whichever
26
+ * their last set of instructions showed. Reading only one spelling silently
27
+ * ignores a proxy the user believes they configured, which is the whole failure
28
+ * this module exists to prevent, so both are accepted with uppercase winning a
29
+ * conflict.
30
+ */
31
+ function readProxyEnv(env, name) {
32
+ const upper = env[name.toUpperCase()];
33
+ if (typeof upper === 'string' && upper.trim())
34
+ return upper.trim();
35
+ const lower = env[name.toLowerCase()];
36
+ if (typeof lower === 'string' && lower.trim())
37
+ return lower.trim();
38
+ return undefined;
39
+ }
40
+ /**
41
+ * Whether NO_PROXY exempts this host, following the de-facto rules curl uses:
42
+ * a comma-separated list, `*` meaning everything, a leading dot ignored, and a
43
+ * bare entry matching that host plus its subdomains. An entry may pin a port,
44
+ * in which case the port must match too.
45
+ */
46
+ export function isProxyBypassed(host, port, noProxy) {
47
+ if (!noProxy)
48
+ return false;
49
+ const entries = noProxy.split(',').map((e) => e.trim().toLowerCase()).filter(Boolean);
50
+ if (entries.includes('*'))
51
+ return true;
52
+ const target = host.toLowerCase();
53
+ return entries.some((entry) => {
54
+ let pattern = entry;
55
+ let entryPort;
56
+ // Only split a port off when it is unambiguous; a bare IPv6 literal is full
57
+ // of colons and must not be chopped at the first one.
58
+ const lastColon = pattern.lastIndexOf(':');
59
+ if (lastColon > 0 && !pattern.slice(lastColon + 1).includes(']') && /^\d+$/.test(pattern.slice(lastColon + 1))) {
60
+ entryPort = pattern.slice(lastColon + 1);
61
+ pattern = pattern.slice(0, lastColon);
62
+ }
63
+ if (entryPort !== undefined && entryPort !== port)
64
+ return false;
65
+ if (pattern.startsWith('.'))
66
+ pattern = pattern.slice(1);
67
+ return target === pattern || target.endsWith(`.${pattern}`);
68
+ });
69
+ }
70
+ /**
71
+ * The proxy URL that applies to `targetUrl`, or undefined for a direct connection.
72
+ *
73
+ * `wss:`/`ws:` are resolved as their HTTP equivalents because that is how the
74
+ * connection is actually made — a WebSocket starts life as an HTTP upgrade, so
75
+ * a user who set HTTPS_PROXY expects `wss://` to go through it.
76
+ */
77
+ export function resolveProxyUrl(targetUrl, env = process.env) {
78
+ const target = new URL(targetUrl);
79
+ const isSecure = target.protocol === 'https:' || target.protocol === 'wss:';
80
+ const port = target.port || (isSecure ? '443' : '80');
81
+ if (isProxyBypassed(target.hostname, port, readProxyEnv(env, 'NO_PROXY')))
82
+ return undefined;
83
+ const specific = isSecure ? readProxyEnv(env, 'HTTPS_PROXY') : readProxyEnv(env, 'HTTP_PROXY');
84
+ return specific ?? readProxyEnv(env, 'ALL_PROXY');
85
+ }
86
+ /**
87
+ * An agent that routes a connection to `targetUrl` through the configured proxy,
88
+ * or undefined when no proxy applies and the connection should go out directly.
89
+ *
90
+ * The result is an `http.Agent`, which is the one shape both callers accept:
91
+ * `https.request` takes it as `agent`, and `ws` takes the same object under the
92
+ * same name. That is why this package talks to the API over `node:https` rather
93
+ * than global `fetch` — `fetch` cannot be handed one of these, so using it would
94
+ * mean two separate proxy implementations for the two transports.
95
+ *
96
+ * A malformed proxy URL throws rather than falling back to a direct connection.
97
+ * On a machine that can only reach the internet through a proxy the direct
98
+ * attempt fails anyway, and it fails with a connection error that says nothing
99
+ * about the real cause; naming the bad setting is far more useful.
100
+ */
101
+ export function proxyAgentFor(targetUrl, env = process.env) {
102
+ const proxyUrl = resolveProxyUrl(targetUrl, env);
103
+ if (!proxyUrl)
104
+ return undefined;
105
+ let protocol;
106
+ try {
107
+ protocol = new URL(proxyUrl).protocol;
108
+ }
109
+ catch {
110
+ throw new CcbError(`Proxy setting is not a valid URL: ${proxyUrl}`, 'invalid_proxy', 'Expected something like http://host:port or socks5://host:port');
111
+ }
112
+ if (SOCKS_PROTOCOLS.has(protocol))
113
+ return new SocksProxyAgent(proxyUrl);
114
+ if (protocol === 'http:' || protocol === 'https:') {
115
+ // HttpsProxyAgent covers both, because every endpoint this package talks to
116
+ // is https/wss and therefore always reached by a CONNECT tunnel.
117
+ return new HttpsProxyAgent(proxyUrl);
118
+ }
119
+ throw new CcbError(`Unsupported proxy scheme "${protocol.replace(':', '')}" in ${proxyUrl}`, 'invalid_proxy', 'Supported schemes: http, https, socks, socks4, socks4a, socks5, socks5h');
120
+ }
121
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import WebSocket from 'ws';
2
2
  import { getCredentials, getAccessToken } from '../battery/auth/index.js';
3
3
  import { CcbError } from '../battery/errors.js';
4
+ import { proxyAgentFor } from '../proxy.js';
4
5
  import { DEFAULT_KEYTERMS, packKeyterms } from './keyterms.js';
5
6
  import { AUDIO_FORMAT, TranscriptMessageType, } from './types.js';
6
7
  const VOICE_STREAM_URL = 'wss://api.anthropic.com/api/ws/speech_to_text/voice_stream';
@@ -43,13 +44,19 @@ export async function openSpeechToTextStream(handlers, options = {}, token) {
43
44
  stt_provider: 'deepgram-nova3',
44
45
  });
45
46
  const keyterms = packKeyterms([...DEFAULT_KEYTERMS, ...(options.extraKeyterms ?? [])]);
46
- const socket = new WebSocket(`${VOICE_STREAM_URL}?${query.toString()}`, {
47
+ const streamUrl = `${VOICE_STREAM_URL}?${query.toString()}`;
48
+ const socket = new WebSocket(streamUrl, {
47
49
  headers: {
48
50
  Authorization: `Bearer ${accessToken}`,
49
51
  'x-app': 'swttch',
50
52
  'anthropic-client-platform': 'claude_code_swttch',
51
53
  ...(keyterms ? { 'x-config-keyterms': keyterms } : {}),
52
54
  },
55
+ // A WebSocket opens as an HTTP upgrade, so it needs the same proxy the usage
56
+ // requests use. Without this, dictation is dead on any machine that reaches
57
+ // the internet only through a proxy, even though the usage panel works.
58
+ // undefined here means "no proxy configured" and ws connects directly.
59
+ agent: proxyAgentFor(streamUrl),
53
60
  });
54
61
  let keepalive = null;
55
62
  let closing = false;
@@ -1 +1 @@
1
- {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/stt/stream.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/stt/stream.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swttch/extend-kit",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Companion tools for Swttch that live outside the plugin bundle: Claude Code account/usage SDK, the ccb CLI, and speech-to-text.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -60,6 +60,8 @@
60
60
  "typescript": "^6.0.2"
61
61
  },
62
62
  "dependencies": {
63
+ "https-proxy-agent": "^9.1.0",
64
+ "socks-proxy-agent": "^10.1.0",
63
65
  "ws": "^8.21.3"
64
66
  }
65
67
  }