@juspay/neurolink 11.29.2 → 12.0.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.
Files changed (56) hide show
  1. package/CHANGELOG.md +54 -2
  2. package/dist/auth/anthropicOAuth.d.ts +50 -0
  3. package/dist/auth/anthropicOAuth.js +78 -0
  4. package/dist/browser/neurolink.min.js +393 -393
  5. package/dist/cli/commands/proxy.d.ts +2 -0
  6. package/dist/cli/commands/proxy.js +284 -4
  7. package/dist/cli/commands/proxyExpose.d.ts +35 -0
  8. package/dist/cli/commands/proxyExpose.js +252 -0
  9. package/dist/cli/commands/proxyPeer.d.ts +29 -0
  10. package/dist/cli/commands/proxyPeer.js +738 -0
  11. package/dist/cli/commands/proxyShare.d.ts +37 -0
  12. package/dist/cli/commands/proxyShare.js +1080 -0
  13. package/dist/cli/parser.js +7 -1
  14. package/dist/core/baseProvider.js +20 -1
  15. package/dist/neurolink.js +30 -2
  16. package/dist/proxy/peerStore.d.ts +52 -0
  17. package/dist/proxy/peerStore.js +324 -0
  18. package/dist/proxy/peerTransport.d.ts +38 -0
  19. package/dist/proxy/peerTransport.js +242 -0
  20. package/dist/proxy/proxyPaths.d.ts +8 -0
  21. package/dist/proxy/proxyPaths.js +55 -17
  22. package/dist/proxy/requestLogger.js +8 -0
  23. package/dist/proxy/residentGrants.d.ts +57 -0
  24. package/dist/proxy/residentGrants.js +393 -0
  25. package/dist/proxy/shareAudit.d.ts +81 -0
  26. package/dist/proxy/shareAudit.js +280 -0
  27. package/dist/proxy/shareContext.d.ts +38 -0
  28. package/dist/proxy/shareContext.js +92 -0
  29. package/dist/proxy/shareGate.d.ts +64 -0
  30. package/dist/proxy/shareGate.js +216 -0
  31. package/dist/proxy/shareGrants.d.ts +115 -0
  32. package/dist/proxy/shareGrants.js +590 -0
  33. package/dist/proxy/shareLease.d.ts +101 -0
  34. package/dist/proxy/shareLease.js +192 -0
  35. package/dist/proxy/shareLedger.d.ts +105 -0
  36. package/dist/proxy/shareLedger.js +406 -0
  37. package/dist/proxy/shareListener.d.ts +60 -0
  38. package/dist/proxy/shareListener.js +143 -0
  39. package/dist/proxy/shareNotes.d.ts +97 -0
  40. package/dist/proxy/shareNotes.js +234 -0
  41. package/dist/proxy/sharePolicy.d.ts +110 -0
  42. package/dist/proxy/sharePolicy.js +366 -0
  43. package/dist/proxy/shareProvisioning.d.ts +110 -0
  44. package/dist/proxy/shareProvisioning.js +237 -0
  45. package/dist/proxy/shareReceipts.d.ts +99 -0
  46. package/dist/proxy/shareReceipts.js +303 -0
  47. package/dist/proxy/shareSigning.d.ts +40 -0
  48. package/dist/proxy/shareSigning.js +78 -0
  49. package/dist/server/routes/claudeProxyRoutes.js +1066 -3
  50. package/dist/types/cli.d.ts +61 -0
  51. package/dist/types/proxy.d.ts +781 -0
  52. package/dist/utils/streamCancellation.d.ts +46 -0
  53. package/dist/utils/streamCancellation.js +90 -0
  54. package/dist/utils/ttsStream.js +34 -1
  55. package/dist/voice/livekit/realtimeEventBridge.js +27 -6
  56. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -1,8 +1,60 @@
1
- ## [11.29.2](https://github.com/juspay/neurolink/compare/v11.29.1...v11.29.2) (2026-08-25)
1
+ ## [12.0.0](https://github.com/juspay/neurolink/compare/v11.30.0...v12.0.0) (2026-08-25)
2
+
3
+ ### ⚠ BREAKING CHANGES
4
+
5
+ - **(tts):** out of a TTS-enabled stream blocked the consumer permanently:
6
+
7
+ pnpm run test:tts:unit 2.95s user 1% cpu 5:09.80 total
8
+
9
+ 1% CPU over five minutes is not slow work — nothing was running. The
10
+ process sat idle in teardown.
11
+
12
+ interleaveTTSStream's finally awaited Promise.allSettled on the source
13
+ and audio iterators' .return(). `.return()` on an async generator parked
14
+ inside an `await` does not interrupt it: the request is queued behind the
15
+ in-flight next() and only runs when the generator next suspends. A
16
+ provider stream is wrapped several times — lifecycle in baseProvider, MCP
17
+ and pool wrappers in neurolink — and during a pull every one of those
18
+ layers sits in an `await` on the layer below. None can unwind, so those
19
+ promises settle never, not late. Instrumented, at the hang:
20
+
21
+ finally ENTERED completed=false nextSource=true nextAudio=true
22
+ awaiting allSettled of 2 release(s)
23
+ calling sourceIterator.return()
24
+ calling audioIterator.return()
25
+ <neither ever resolved>
26
+
27
+ Two changes, because the generator protocol alone cannot express this.
28
+
29
+ streamCancellation.ts adds an out-of-band channel. A wrapper registers a
30
+ cancel callback on the stream object it returns; the callback closes its
31
+ own upstream iterator and forwards the request downward. These are plain
32
+ function calls, not generator protocol, so they run immediately and do not
33
+ depend on any pending next() settling. baseProvider hoists its upstream
34
+ iterator — iterating the hoisted handle is equivalent to iterating the
35
+ original, it just leaves something reachable from outside the generator —
36
+ and registers a hook. interleaveTTSStream fires it on abandonment before
37
+ asking politely via .return().
38
+
39
+ Registration is optional and reading it is defensive, so a stream that
40
+ knows nothing about this behaves exactly as before.
41
+
42
+ Teardown is also now bounded. Even with the channel, this module cannot
43
+ assume every upstream cooperates, and a consumer's exit must not depend on
44
+ one that may never answer. The releases are still issued and still
45
+ propagate whenever the upstream is responsive.
46
+
47
+ test:tts:unit 327s exit 0, 1 test skipped -> 83s exit 0, 43 passed
48
+
49
+ That test had never passed. It and the cleanup it probes both arrived in
2
50
 
3
51
  ### Bug Fixes
4
52
 
5
- - **(media):** propagate cancellation through the video generation chain ([3bcaff8](https://github.com/juspay/neurolink/commit/3bcaff8876c85705383a8c59f5b4910138ea608b))
53
+ - **(tts):** stop early stream break from hanging the caller forever ([c8f3d5b](https://github.com/juspay/neurolink/commit/c8f3d5b0ab71621916ec7d9d6970243ff916cae8)), closes [#1336](https://github.com/juspay/neurolink/issues/1336)
54
+
55
+ ### Performance Improvements
56
+
57
+ - **(ci):** balance extended-suite shards by measured duration, not count ([ed7c300](https://github.com/juspay/neurolink/commit/ed7c300b4171dd2db4dab59e4ef18dfd9477110c))
6
58
 
7
59
  ## [11.2.3](https://github.com/juspay/neurolink/compare/v11.2.2...v11.2.3) (2026-08-19)
8
60
 
@@ -349,6 +349,56 @@ export declare function stopCallbackServer(server: Server): Promise<void>;
349
349
  * const authUrl = oauth.generateAuthUrl({ codeChallenge });
350
350
  * ```
351
351
  */
352
+ /**
353
+ * The scopes a Claude Pro/Max subscription login asks for.
354
+ *
355
+ * Narrower than {@link DEFAULT_SCOPES}: the subscription flow does not create
356
+ * API keys, so `org:create_api_key` would be asking for a permission nothing
357
+ * downstream uses.
358
+ */
359
+ export declare const SUBSCRIPTION_SCOPES = "user:profile user:inference";
360
+ /**
361
+ * Build the Claude Pro/Max authorization URL for a caller-supplied challenge.
362
+ *
363
+ * Extracted so the subscription login and split-PKCE provisioning cannot drift
364
+ * apart: they must produce byte-identical URLs but for the challenge and state,
365
+ * and a second hand-rolled copy is how one of them ends up missing `code=true`
366
+ * and silently redirecting instead of showing a code.
367
+ *
368
+ * **`state` is not the verifier here.** The interactive login sets `state` to
369
+ * the verifier as a convenience, which is safe when one machine holds both. In
370
+ * split provisioning it would hand the verifier to the party that must not have
371
+ * it, so the caller supplies an unrelated random state and keeps its verifier.
372
+ */
373
+ export declare function buildSubscriptionAuthUrl(params: {
374
+ codeChallenge: string;
375
+ state: string;
376
+ clientId?: string;
377
+ redirectUri?: string;
378
+ scope?: string;
379
+ }): string;
380
+ /**
381
+ * Exchange an authorization code for subscription tokens.
382
+ *
383
+ * The token endpoint wants a JSON body, not form encoding, and it wants the
384
+ * verifier that produced the challenge in the authorization URL. In split
385
+ * provisioning that verifier lives only on the borrower's machine, which is the
386
+ * entire point: the code alone buys nothing.
387
+ */
388
+ export declare function exchangeSubscriptionCode(params: {
389
+ code: string;
390
+ state: string;
391
+ codeVerifier: string;
392
+ clientId?: string;
393
+ redirectUri?: string;
394
+ }): Promise<{
395
+ accessToken: string;
396
+ refreshToken?: string;
397
+ expiresAt?: number;
398
+ tokenType: string;
399
+ scope?: string;
400
+ accountEmail?: string;
401
+ }>;
352
402
  export declare function createAnthropicOAuth(overrides?: Partial<AnthropicOAuthConfig>): AnthropicOAuth;
353
403
  /**
354
404
  * Anthropic OAuth configuration creator for providerConfig pattern
@@ -1046,6 +1046,84 @@ export async function stopCallbackServer(server) {
1046
1046
  * const authUrl = oauth.generateAuthUrl({ codeChallenge });
1047
1047
  * ```
1048
1048
  */
1049
+ /**
1050
+ * The scopes a Claude Pro/Max subscription login asks for.
1051
+ *
1052
+ * Narrower than {@link DEFAULT_SCOPES}: the subscription flow does not create
1053
+ * API keys, so `org:create_api_key` would be asking for a permission nothing
1054
+ * downstream uses.
1055
+ */
1056
+ export const SUBSCRIPTION_SCOPES = "user:profile user:inference";
1057
+ /**
1058
+ * Build the Claude Pro/Max authorization URL for a caller-supplied challenge.
1059
+ *
1060
+ * Extracted so the subscription login and split-PKCE provisioning cannot drift
1061
+ * apart: they must produce byte-identical URLs but for the challenge and state,
1062
+ * and a second hand-rolled copy is how one of them ends up missing `code=true`
1063
+ * and silently redirecting instead of showing a code.
1064
+ *
1065
+ * **`state` is not the verifier here.** The interactive login sets `state` to
1066
+ * the verifier as a convenience, which is safe when one machine holds both. In
1067
+ * split provisioning it would hand the verifier to the party that must not have
1068
+ * it, so the caller supplies an unrelated random state and keeps its verifier.
1069
+ */
1070
+ export function buildSubscriptionAuthUrl(params) {
1071
+ const url = new URL(ANTHROPIC_AUTH_URL);
1072
+ url.searchParams.set("code", "true");
1073
+ url.searchParams.set("client_id", params.clientId ?? CLAUDE_CODE_CLIENT_ID);
1074
+ url.searchParams.set("response_type", "code");
1075
+ url.searchParams.set("redirect_uri", params.redirectUri ?? ANTHROPIC_REDIRECT_URI);
1076
+ url.searchParams.set("scope", params.scope ?? SUBSCRIPTION_SCOPES);
1077
+ url.searchParams.set("code_challenge", params.codeChallenge);
1078
+ url.searchParams.set("code_challenge_method", "S256");
1079
+ url.searchParams.set("state", params.state);
1080
+ return url.toString();
1081
+ }
1082
+ /**
1083
+ * Exchange an authorization code for subscription tokens.
1084
+ *
1085
+ * The token endpoint wants a JSON body, not form encoding, and it wants the
1086
+ * verifier that produced the challenge in the authorization URL. In split
1087
+ * provisioning that verifier lives only on the borrower's machine, which is the
1088
+ * entire point: the code alone buys nothing.
1089
+ */
1090
+ export async function exchangeSubscriptionCode(params) {
1091
+ const response = await fetch(ANTHROPIC_TOKEN_URL, {
1092
+ method: "POST",
1093
+ headers: { "Content-Type": "application/json" },
1094
+ body: JSON.stringify({
1095
+ code: params.code,
1096
+ state: params.state,
1097
+ grant_type: "authorization_code",
1098
+ client_id: params.clientId ?? CLAUDE_CODE_CLIENT_ID,
1099
+ redirect_uri: params.redirectUri ?? ANTHROPIC_REDIRECT_URI,
1100
+ code_verifier: params.codeVerifier,
1101
+ }),
1102
+ });
1103
+ if (!response.ok) {
1104
+ const detail = await response.text().catch(() => "");
1105
+ throw new OAuthError(`Token exchange failed: ${response.status} ${detail.slice(0, 300)}`, "TOKEN_EXCHANGE_FAILED");
1106
+ }
1107
+ const data = (await response.json());
1108
+ // A 200 with no token is not a success. Returning `undefined` here would be
1109
+ // written to the token store as a credential and only fail on first use, by
1110
+ // which point the authorization code that could have fixed it is spent.
1111
+ if (typeof data.access_token !== "string" || data.access_token === "") {
1112
+ throw new OAuthError("Token exchange returned no access token.", "TOKEN_EXCHANGE_FAILED");
1113
+ }
1114
+ return {
1115
+ accessToken: data.access_token,
1116
+ ...(data.refresh_token ? { refreshToken: data.refresh_token } : {}),
1117
+ ...(data.expires_in
1118
+ ? { expiresAt: Date.now() + data.expires_in * 1000 }
1119
+ : {}),
1120
+ tokenType: data.token_type || "Bearer",
1121
+ ...(data.scope ? { scope: data.scope } : {}),
1122
+ ...(data.account?.email_address
1123
+ ? { accountEmail: data.account.email_address }
1124
+ : {}),
1125
+ };
1126
+ }
1049
1127
  export function createAnthropicOAuth(overrides = {}) {
1050
1128
  return new AnthropicOAuth(overrides);
1051
1129
  }