@juspay/neurolink 11.29.2 → 11.30.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 (50) hide show
  1. package/CHANGELOG.md +3 -3
  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/proxy/peerStore.d.ts +52 -0
  15. package/dist/proxy/peerStore.js +324 -0
  16. package/dist/proxy/peerTransport.d.ts +38 -0
  17. package/dist/proxy/peerTransport.js +242 -0
  18. package/dist/proxy/proxyPaths.d.ts +8 -0
  19. package/dist/proxy/proxyPaths.js +55 -17
  20. package/dist/proxy/requestLogger.js +8 -0
  21. package/dist/proxy/residentGrants.d.ts +57 -0
  22. package/dist/proxy/residentGrants.js +393 -0
  23. package/dist/proxy/shareAudit.d.ts +81 -0
  24. package/dist/proxy/shareAudit.js +280 -0
  25. package/dist/proxy/shareContext.d.ts +38 -0
  26. package/dist/proxy/shareContext.js +92 -0
  27. package/dist/proxy/shareGate.d.ts +64 -0
  28. package/dist/proxy/shareGate.js +216 -0
  29. package/dist/proxy/shareGrants.d.ts +115 -0
  30. package/dist/proxy/shareGrants.js +590 -0
  31. package/dist/proxy/shareLease.d.ts +101 -0
  32. package/dist/proxy/shareLease.js +192 -0
  33. package/dist/proxy/shareLedger.d.ts +105 -0
  34. package/dist/proxy/shareLedger.js +406 -0
  35. package/dist/proxy/shareListener.d.ts +60 -0
  36. package/dist/proxy/shareListener.js +143 -0
  37. package/dist/proxy/shareNotes.d.ts +97 -0
  38. package/dist/proxy/shareNotes.js +234 -0
  39. package/dist/proxy/sharePolicy.d.ts +110 -0
  40. package/dist/proxy/sharePolicy.js +366 -0
  41. package/dist/proxy/shareProvisioning.d.ts +110 -0
  42. package/dist/proxy/shareProvisioning.js +237 -0
  43. package/dist/proxy/shareReceipts.d.ts +99 -0
  44. package/dist/proxy/shareReceipts.js +303 -0
  45. package/dist/proxy/shareSigning.d.ts +40 -0
  46. package/dist/proxy/shareSigning.js +78 -0
  47. package/dist/server/routes/claudeProxyRoutes.js +1066 -3
  48. package/dist/types/cli.d.ts +61 -0
  49. package/dist/types/proxy.d.ts +781 -0
  50. package/package.json +2 -1
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Leases — how a lender keeps control of a credential that lives on someone
3
+ * else's machine.
4
+ *
5
+ * In **live** sharing the lender's gate is in the request path, so control is
6
+ * immediate and total. **Complete** sharing trades that away: the borrower holds
7
+ * its own credential on the lender's account and calls the upstream directly, so
8
+ * the lender is not consulted per request. What is left is this — a signed,
9
+ * time-boxed statement of consent that the borrower enforces on itself and must
10
+ * keep renewing.
11
+ *
12
+ * The design turns on one number: **how long may the borrower run without
13
+ * hearing from me.** `offlineGraceMs` is that number. Set it to zero and
14
+ * complete mode collapses into live mode's availability; set it to a day and the
15
+ * borrower keeps working through a weekend when the lender's laptop is shut, at
16
+ * the cost of a day's revocation latency. Both are legitimate; neither is free.
17
+ *
18
+ * `notAfter` is the backstop that does not depend on the borrower's cooperation
19
+ * at all — it is baked into the signed payload, so a borrower that simply never
20
+ * calls home still stops.
21
+ *
22
+ * **Signing.** HMAC-SHA256 keyed by a per-grant secret shared with the borrower
23
+ * at provisioning time. Asymmetric signatures would be tidier, but the key
24
+ * distribution problem they solve does not exist here — exactly two parties are
25
+ * involved and they already share a secret — and the package's browser bundle
26
+ * stubs `node:crypto` down to a subset with no Ed25519 in it.
27
+ *
28
+ * @module proxy/shareLease
29
+ */
30
+ import { generateShareSecret, secretsMatch, signSharePayload, } from "./shareSigning.js";
31
+ /** A week: long enough to survive a holiday, short enough to be a real bound. */
32
+ export const DEFAULT_LEASE_TTL_MS = 604_800_000;
33
+ /** Fifteen minutes — frequent enough that a pause lands the same session. */
34
+ export const DEFAULT_HEARTBEAT_MS = 900_000;
35
+ /** Twenty-four hours of running unheard-from. The headline trade-off. */
36
+ export const DEFAULT_OFFLINE_GRACE_MS = 86_400_000;
37
+ /**
38
+ * Narrow a verdict to its refusing half.
39
+ *
40
+ * One of the package's build steps compiles without `strictNullChecks`, where
41
+ * TypeScript will not narrow a boolean discriminant. An explicit predicate holds
42
+ * in both modes — see the same pattern in `sharePolicy.isShareRefusal`.
43
+ */
44
+ export function isLeaseRefusal(verdict) {
45
+ return !verdict.usable;
46
+ }
47
+ export function generateLeaseSecret() {
48
+ return generateShareSecret();
49
+ }
50
+ /**
51
+ * The signed portion of a lease — everything but the signature itself.
52
+ *
53
+ * Field order is irrelevant: `signSharePayload` canonicalises before hashing.
54
+ * The explicit list is still worth keeping, because it says which fields a
55
+ * borrower is entitled to rely on rather than signing whatever happens to be on
56
+ * the object.
57
+ */
58
+ function leasePayload(lease) {
59
+ return {
60
+ schemaVersion: lease.schemaVersion,
61
+ grantId: lease.grantId,
62
+ peerLabel: lease.peerLabel,
63
+ issuedAt: lease.issuedAt,
64
+ notAfter: lease.notAfter,
65
+ heartbeatEveryMs: lease.heartbeatEveryMs,
66
+ offlineGraceMs: lease.offlineGraceMs,
67
+ gates: lease.gates,
68
+ entitlementSnapshot: lease.entitlementSnapshot,
69
+ };
70
+ }
71
+ function sign(payload, secret) {
72
+ return signSharePayload(payload, secret);
73
+ }
74
+ /**
75
+ * Issue a lease for a grant.
76
+ *
77
+ * The gates are snapshotted rather than referenced: the borrower enforces what
78
+ * the lender agreed to at issue time, and a tightened policy reaches them at the
79
+ * next heartbeat rather than silently mid-lease.
80
+ */
81
+ export function issueLease(grant, now = Date.now()) {
82
+ const policy = grant.leasePolicy;
83
+ const secret = grant.leaseSecret;
84
+ if (!secret) {
85
+ throw new Error(`Grant ${grant.id} has no lease secret — provision it before issuing a lease.`);
86
+ }
87
+ const ttl = policy?.ttlMs ?? DEFAULT_LEASE_TTL_MS;
88
+ // A lease can never outlive the grant's own expiry.
89
+ const notAfter = Math.min(now + ttl, grant.gates.notAfter ?? Number.POSITIVE_INFINITY);
90
+ const unsigned = {
91
+ schemaVersion: 1,
92
+ grantId: grant.id,
93
+ peerLabel: grant.peerLabel,
94
+ issuedAt: now,
95
+ notAfter,
96
+ heartbeatEveryMs: policy?.heartbeatEveryMs ?? DEFAULT_HEARTBEAT_MS,
97
+ offlineGraceMs: policy?.offlineGraceMs ?? DEFAULT_OFFLINE_GRACE_MS,
98
+ gates: grant.gates,
99
+ entitlementSnapshot: grant.entitlement.ledger === "coins"
100
+ ? (grant.entitlement.coins ?? 0)
101
+ : "unlimited",
102
+ };
103
+ return { ...unsigned, signature: sign(leasePayload(unsigned), secret) };
104
+ }
105
+ /** Does this lease actually come from the lender it claims to? */
106
+ export function isLeaseAuthentic(lease, secret) {
107
+ const { signature, ...unsigned } = lease;
108
+ return secretsMatch(sign(leasePayload(unsigned), secret), signature);
109
+ }
110
+ /**
111
+ * May the borrower serve from this lease right now?
112
+ *
113
+ * Three independent stops, in the order that matters:
114
+ *
115
+ * 1. **Unsigned** — someone edited the file. Nothing else is worth checking.
116
+ * 2. **Expired** — `notAfter` passed. Immune to a borrower that never checks in.
117
+ * 3. **Grace elapsed** — the lender has been unreachable for longer than it
118
+ * agreed to be trusted for. This is the one that makes "the lender turned
119
+ * their laptop off" survivable and "the lender revoked me" eventually
120
+ * binding.
121
+ */
122
+ export function evaluateLease(args) {
123
+ const now = args.now ?? Date.now();
124
+ if (!isLeaseAuthentic(args.lease, args.secret)) {
125
+ return {
126
+ usable: false,
127
+ reason: "unsigned",
128
+ detail: "lease signature does not match the lender's secret",
129
+ };
130
+ }
131
+ if (args.lease.notAfter <= now) {
132
+ return {
133
+ usable: false,
134
+ reason: "expired",
135
+ detail: "the lease's hard expiry has passed",
136
+ };
137
+ }
138
+ // The lease's own issue time is the first heartbeat: a freshly provisioned
139
+ // borrower has not failed to check in, it simply has not had to yet.
140
+ const lastContact = args.lastHeartbeatAt ?? args.lease.issuedAt;
141
+ const graceDeadline = lastContact + args.lease.offlineGraceMs;
142
+ if (graceDeadline <= now) {
143
+ return {
144
+ usable: false,
145
+ reason: "grace_elapsed",
146
+ detail: "the lender has been unreachable for longer than this lease allows",
147
+ };
148
+ }
149
+ return {
150
+ usable: true,
151
+ nextHeartbeatDueAt: lastContact + args.lease.heartbeatEveryMs,
152
+ };
153
+ }
154
+ /** Should the borrower check in now? */
155
+ export function isHeartbeatDue(lease, lastHeartbeatAt, now = Date.now()) {
156
+ const lastContact = lastHeartbeatAt ?? lease.issuedAt;
157
+ return now - lastContact >= lease.heartbeatEveryMs;
158
+ }
159
+ /** The gates a resident grant must enforce on itself, from its lease. */
160
+ export function leasedGates(lease) {
161
+ return lease.gates;
162
+ }
163
+ /**
164
+ * Assemble what the borrower collects once the lender has authorized.
165
+ *
166
+ * Carries no token, by construction. The borrower already holds the verifier
167
+ * that turns the enclosed code into tokens, and it exchanges the two on its own
168
+ * machine — so nothing here is worth intercepting, and the lender never holds a
169
+ * credential for the account it just authorized.
170
+ *
171
+ * `accountLabel` is the lender's *suggestion*, not the key the credential ends
172
+ * up under. The borrower names the account itself — `<peer>-shared` unless
173
+ * `--label` says otherwise — and refuses to install over an existing one,
174
+ * because Anthropic quota snapshots are keyed by the bare label and two
175
+ * accounts sharing one would merge each other's windows. Derived from both
176
+ * parties' names so that a lender running `share list` and a borrower running
177
+ * `auth list` are looking at recognisably the same thing.
178
+ */
179
+ export function buildProvisionClaim(args) {
180
+ const now = args.now ?? Date.now();
181
+ if (!args.grant.leaseSecret) {
182
+ throw new Error(`Grant ${args.grant.id} has no lease secret — attach lease material first.`);
183
+ }
184
+ return {
185
+ code: args.code,
186
+ state: args.state,
187
+ accountLabel: `${args.grant.peerLabel}-via-${args.lenderName}`,
188
+ leaseSecret: args.grant.leaseSecret,
189
+ lease: issueLease(args.grant, now),
190
+ lenderUrl: (args.lenderUrl ?? "").replace(/\/+$/, ""),
191
+ };
192
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * NeuroCoin ledger and per-window consumption tracking for borrowed traffic.
3
+ *
4
+ * Two jobs, deliberately in one module because they settle from the same event:
5
+ *
6
+ * 1. **Coins.** A grant with a coin entitlement spends against a balance. The
7
+ * balance itself lives on the grant (one source of truth); this module holds
8
+ * the entries and drives the deduction.
9
+ * 2. **Window buckets.** How much of an account's 5h/7d window a grant has taken,
10
+ * keyed by that window's reset timestamp so the counter starts fresh when the
11
+ * window does. This is what makes a slice ceiling mean "a fifth of *this*
12
+ * window" rather than "a fifth, once, forever".
13
+ *
14
+ * **Why hold-then-settle.** Real usage is only known when the response finishes
15
+ * — for a stream, at `message_delta`. A balance checked at admission and
16
+ * deducted at completion lets N concurrent streams each pass the same check and
17
+ * overspend by N-1 requests. So admission opens a *hold* for an estimate, the
18
+ * available balance is `balance - Σ open holds`, and settlement replaces the
19
+ * hold with the real figure. A crash loses in-flight holds, which is the safe
20
+ * direction: the balance is only ever reduced by traffic that actually happened.
21
+ *
22
+ * @module proxy/shareLedger
23
+ */
24
+ import type { ProxyShareGrant, ProxyShareGrantUsageSummary, ProxyShareHold, ProxySharePoolUsage, ProxyShareSettlement, ProxyShareUsage, ProxyShareWindowObservation } from "../types/index.js";
25
+ /** Normalized tokens per coin. */
26
+ export declare const TOKENS_PER_COIN = 1000;
27
+ export declare function initShareLedger(ledgerFilePath: string): void;
28
+ export declare function modelCoinWeight(model: string | undefined): number;
29
+ /** Convert real usage into coins. Pure — the pricing table is the whole story. */
30
+ export declare function usageToCoins(usage: ProxyShareUsage, model: string | undefined): number;
31
+ /**
32
+ * What to hold at admission, before anything is known about the response.
33
+ *
34
+ * Intentionally rough: the hold exists to stop concurrent streams from each
35
+ * spending the last coin, not to predict the bill. Settlement replaces it with
36
+ * the real figure moments later.
37
+ */
38
+ export declare function estimateHoldCoins(model: string | undefined, maxTokens: number | undefined): number;
39
+ /** Coins currently held against a grant by in-flight requests. */
40
+ export declare function heldCoins(grantId: string): number;
41
+ /**
42
+ * Balance a new request may draw on: the stored balance minus what in-flight
43
+ * requests have already claimed.
44
+ */
45
+ export declare function availableCoins(grant: ProxyShareGrant): number;
46
+ export declare function openShareHold(grantId: string, coins: number, now?: number): ProxyShareHold;
47
+ /** Drop a hold without spending it — the request never reached the upstream. */
48
+ export declare function releaseShareHold(holdId: string | undefined): void;
49
+ /**
50
+ * Record how much of an account's windows a borrowed request consumed.
51
+ *
52
+ * Never throws: a bookkeeping failure must not turn a response the borrower has
53
+ * already received into an error.
54
+ */
55
+ export declare function recordShareWindowDelta(observation: ProxyShareWindowObservation): Promise<void>;
56
+ /**
57
+ * Charge a finished borrowed request against its grant and close the hold.
58
+ *
59
+ * Never throws, for the same reason as above.
60
+ */
61
+ export declare function settleShareUsage(settlement: ProxyShareSettlement): Promise<number>;
62
+ /**
63
+ * What a grant has taken from one account's current windows.
64
+ *
65
+ * Buckets whose reset timestamp no longer matches the account's are reported as
66
+ * zero: they describe a window that has since rolled over.
67
+ */
68
+ export declare function readShareWindowUsage(grantId: string, accountKey: string, currentSessionResetAt: number | null, currentWeeklyResetAt: number | null): Promise<{
69
+ sessionFraction: number;
70
+ weeklyFraction: number;
71
+ }>;
72
+ /**
73
+ * What a grant has taken from the pool as a whole, normalised to one window.
74
+ *
75
+ * Summing the per-account fractions and dividing by the account count is what
76
+ * makes a ceiling mean the same thing on a one-account pool and a ten-account
77
+ * one. Without the division, `--max-slice 20` would silently grant 20% of every
78
+ * credential — 200% of a single window's worth across ten accounts.
79
+ *
80
+ * Buckets whose reset timestamp no longer matches contribute zero: they
81
+ * describe a window that has since rolled over.
82
+ */
83
+ export declare function readSharePoolWindowUsage(grantId: string, accounts: ReadonlyArray<{
84
+ accountKey: string;
85
+ sessionResetAt: number | null;
86
+ weeklyResetAt: number | null;
87
+ }>): Promise<ProxySharePoolUsage>;
88
+ /** Per-grant rollup across accounts, for `share status`. */
89
+ export declare function summarizeGrantUsage(grantId: string): Promise<ProxyShareGrantUsageSummary>;
90
+ /**
91
+ * Apply a standing allowance for every period that has elapsed.
92
+ *
93
+ * Called opportunistically at admission — there is no timer, so a node that is
94
+ * off across one or more refill boundaries pays them on its next borrowed
95
+ * request. Paying only the newest period would quietly turn "100 a week" into
96
+ * "100 whenever you next happen to ask", which is not what the operator wrote.
97
+ *
98
+ * `lastAt` advances by whole periods rather than to `now`, so the schedule stays
99
+ * anchored to when the grant was issued instead of drifting later with every
100
+ * catch-up. It advances past the periods the cap refused to pay as well — see
101
+ * {@link MAX_REFILL_CATCHUP_PERIODS}.
102
+ */
103
+ export declare function applyRefillIfDue(grant: ProxyShareGrant, now?: number): Promise<ProxyShareGrant>;
104
+ /** Drop all ledger state. Test isolation only. */
105
+ export declare function resetShareLedgerForTests(): void;