@rynfar/meridian 1.72.0 → 1.74.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 (177) hide show
  1. package/README.md +119 -107
  2. package/assets/banner.svg +18 -43
  3. package/assets/how-it-works.svg +40 -86
  4. package/assets/providers/README.md +10 -0
  5. package/assets/providers/antigravity.png +0 -0
  6. package/assets/providers/claude.png +0 -0
  7. package/dist/antigravity-clients/opencode.js +173 -0
  8. package/dist/antigravity-clients/pi.js +21 -0
  9. package/dist/antigravitySetup-35pd5xqn.js +191 -0
  10. package/dist/cli-1jstkdj9.js +30 -0
  11. package/dist/{cli-9e5cxp89.js → cli-2h3w5n6g.js} +163 -65
  12. package/dist/cli-3zwfvexc.js +56 -0
  13. package/dist/{cli-hxxy0m1z.js → cli-82e2m44k.js} +2 -1
  14. package/dist/cli-8sw5wr9j.js +136 -0
  15. package/dist/{cli-khhjyk04.js → cli-chsp880k.js} +214 -21
  16. package/dist/cli-dh0a1bw4.js +619 -0
  17. package/dist/cli-eehk3052.js +57 -0
  18. package/dist/{cli-ab7zkp1a.js → cli-fveyj34s.js} +32261 -18952
  19. package/dist/cli-grfp0s1j.js +330 -0
  20. package/dist/cli-qpfky8wy.js +133 -0
  21. package/dist/{cli-hj61zpt7.js → cli-wbsmhxcx.js} +26 -2
  22. package/dist/cli.js +111 -38
  23. package/dist/cliDashboard-c0dvkj4y.js +325 -0
  24. package/dist/configDir.d.ts +30 -0
  25. package/dist/configDir.d.ts.map +1 -0
  26. package/dist/organizationName-90f0atkn.js +29 -0
  27. package/dist/{pluginPage-f5gdmbxc.js → pluginPage-pp72d2j8.js} +1 -1
  28. package/dist/{profileCli-1eecbmgk.js → profileCli-1bfgc05h.js} +13 -3
  29. package/dist/{profilePage-mamej7kp.js → profilePage-vs319rvf.js} +252 -60
  30. package/dist/profileRename-4a4km6g8.js +22 -0
  31. package/dist/{profiles-wch9h234.js → profiles-0mp32ek9.js} +16 -3
  32. package/dist/proxy/adapterInstances.d.ts +1 -1
  33. package/dist/proxy/adapterInstances.d.ts.map +1 -1
  34. package/dist/proxy/adapters/claudecode.d.ts.map +1 -1
  35. package/dist/proxy/adapters/droid.d.ts.map +1 -1
  36. package/dist/proxy/auth.d.ts +2 -0
  37. package/dist/proxy/auth.d.ts.map +1 -1
  38. package/dist/proxy/authDiscovery.d.ts +52 -0
  39. package/dist/proxy/authDiscovery.d.ts.map +1 -0
  40. package/dist/proxy/backends/antigravity.d.ts +8 -0
  41. package/dist/proxy/backends/antigravity.d.ts.map +1 -0
  42. package/dist/proxy/backends/antigravityAttachments.d.ts +16 -0
  43. package/dist/proxy/backends/antigravityAttachments.d.ts.map +1 -0
  44. package/dist/proxy/backends/antigravityGrammar.d.ts +12 -0
  45. package/dist/proxy/backends/antigravityGrammar.d.ts.map +1 -0
  46. package/dist/proxy/backends/antigravityJobs.d.ts +19 -0
  47. package/dist/proxy/backends/antigravityJobs.d.ts.map +1 -0
  48. package/dist/proxy/backends/antigravityMedia.d.ts +9 -0
  49. package/dist/proxy/backends/antigravityMedia.d.ts.map +1 -0
  50. package/dist/proxy/backends/antigravityNative.d.ts +8 -0
  51. package/dist/proxy/backends/antigravityNative.d.ts.map +1 -0
  52. package/dist/proxy/backends/antigravityOpenai.d.ts +9 -0
  53. package/dist/proxy/backends/antigravityOpenai.d.ts.map +1 -0
  54. package/dist/proxy/backends/antigravityOpenaiMedia.d.ts +9 -0
  55. package/dist/proxy/backends/antigravityOpenaiMedia.d.ts.map +1 -0
  56. package/dist/proxy/backends/antigravityPlugins.d.ts +41 -0
  57. package/dist/proxy/backends/antigravityPlugins.d.ts.map +1 -0
  58. package/dist/proxy/backends/antigravityProbe.d.ts +16 -0
  59. package/dist/proxy/backends/antigravityProbe.d.ts.map +1 -0
  60. package/dist/proxy/backends/antigravityProcess.d.ts +4 -0
  61. package/dist/proxy/backends/antigravityProcess.d.ts.map +1 -0
  62. package/dist/proxy/backends/antigravityProtocol.d.ts +582 -0
  63. package/dist/proxy/backends/antigravityProtocol.d.ts.map +1 -0
  64. package/dist/proxy/backends/antigravityReplay.d.ts +41 -0
  65. package/dist/proxy/backends/antigravityReplay.d.ts.map +1 -0
  66. package/dist/proxy/backends/antigravityResponses.d.ts +35 -0
  67. package/dist/proxy/backends/antigravityResponses.d.ts.map +1 -0
  68. package/dist/proxy/backends/antigravityRuntime.d.ts +189 -0
  69. package/dist/proxy/backends/antigravityRuntime.d.ts.map +1 -0
  70. package/dist/proxy/backends/antigravitySchema.d.ts +10 -0
  71. package/dist/proxy/backends/antigravitySchema.d.ts.map +1 -0
  72. package/dist/proxy/backends/antigravitySessions.d.ts +28 -0
  73. package/dist/proxy/backends/antigravitySessions.d.ts.map +1 -0
  74. package/dist/proxy/backends/antigravityState.d.ts +22 -0
  75. package/dist/proxy/backends/antigravityState.d.ts.map +1 -0
  76. package/dist/proxy/backends/antigravityStops.d.ts +10 -0
  77. package/dist/proxy/backends/antigravityStops.d.ts.map +1 -0
  78. package/dist/proxy/backends/antigravityTokens.d.ts +14 -0
  79. package/dist/proxy/backends/antigravityTokens.d.ts.map +1 -0
  80. package/dist/proxy/backends/antigravityUrl.d.ts +7 -0
  81. package/dist/proxy/backends/antigravityUrl.d.ts.map +1 -0
  82. package/dist/proxy/backends/providerStatus.d.ts +15 -0
  83. package/dist/proxy/backends/providerStatus.d.ts.map +1 -0
  84. package/dist/proxy/credentialsMode.d.ts +77 -0
  85. package/dist/proxy/credentialsMode.d.ts.map +1 -0
  86. package/dist/proxy/design.d.ts.map +1 -1
  87. package/dist/proxy/followActive.d.ts +262 -0
  88. package/dist/proxy/followActive.d.ts.map +1 -0
  89. package/dist/proxy/limitDetection.d.ts +110 -0
  90. package/dist/proxy/limitDetection.d.ts.map +1 -0
  91. package/dist/proxy/models.d.ts.map +1 -1
  92. package/dist/proxy/oauthPlan.d.ts +63 -0
  93. package/dist/proxy/oauthPlan.d.ts.map +1 -0
  94. package/dist/proxy/oauthUsage.d.ts +65 -2
  95. package/dist/proxy/oauthUsage.d.ts.map +1 -1
  96. package/dist/proxy/openai.d.ts +6 -2
  97. package/dist/proxy/openai.d.ts.map +1 -1
  98. package/dist/proxy/organizationName.d.ts +71 -0
  99. package/dist/proxy/organizationName.d.ts.map +1 -0
  100. package/dist/proxy/passthroughEarlyStop.d.ts +10 -0
  101. package/dist/proxy/passthroughEarlyStop.d.ts.map +1 -1
  102. package/dist/proxy/planAllowance.d.ts +75 -0
  103. package/dist/proxy/planAllowance.d.ts.map +1 -0
  104. package/dist/proxy/probes.d.ts +85 -0
  105. package/dist/proxy/probes.d.ts.map +1 -0
  106. package/dist/proxy/profileCli.d.ts +53 -1
  107. package/dist/proxy/profileCli.d.ts.map +1 -1
  108. package/dist/proxy/profileHealth.d.ts +127 -0
  109. package/dist/proxy/profileHealth.d.ts.map +1 -0
  110. package/dist/proxy/profileRename.d.ts +83 -0
  111. package/dist/proxy/profileRename.d.ts.map +1 -0
  112. package/dist/proxy/profiles.d.ts +72 -6
  113. package/dist/proxy/profiles.d.ts.map +1 -1
  114. package/dist/proxy/query.d.ts +7 -0
  115. package/dist/proxy/query.d.ts.map +1 -1
  116. package/dist/proxy/routing.d.ts +59 -1
  117. package/dist/proxy/routing.d.ts.map +1 -1
  118. package/dist/proxy/sdkFeatures.d.ts +1 -1
  119. package/dist/proxy/sdkFeatures.d.ts.map +1 -1
  120. package/dist/proxy/server.d.ts.map +1 -1
  121. package/dist/proxy/session/fingerprint.d.ts +5 -0
  122. package/dist/proxy/session/fingerprint.d.ts.map +1 -1
  123. package/dist/proxy/session/lineage.d.ts.map +1 -1
  124. package/dist/proxy/setup.d.ts.map +1 -1
  125. package/dist/proxy/supervision.d.ts +41 -0
  126. package/dist/proxy/supervision.d.ts.map +1 -0
  127. package/dist/proxy/tokenRefresh.d.ts +38 -4
  128. package/dist/proxy/tokenRefresh.d.ts.map +1 -1
  129. package/dist/proxy/types.d.ts +32 -0
  130. package/dist/proxy/types.d.ts.map +1 -1
  131. package/dist/server.js +11 -7
  132. package/dist/settings.d.ts +83 -0
  133. package/dist/settings.d.ts.map +1 -0
  134. package/dist/{setup-ndmjpy23.js → setup-7fr49z92.js} +1 -1
  135. package/dist/statusProbe-9x9q261e.js +151 -0
  136. package/dist/telemetry/dashboard.d.ts.map +1 -1
  137. package/dist/telemetry/index.d.ts +20 -2
  138. package/dist/telemetry/index.d.ts.map +1 -1
  139. package/dist/telemetry/landing.d.ts +1 -1
  140. package/dist/telemetry/landing.d.ts.map +1 -1
  141. package/dist/telemetry/logStore.d.ts +11 -0
  142. package/dist/telemetry/logStore.d.ts.map +1 -1
  143. package/dist/telemetry/pricingStore.d.ts +2 -2
  144. package/dist/telemetry/pricingStore.d.ts.map +1 -1
  145. package/dist/telemetry/profileBar.d.ts +3 -3
  146. package/dist/telemetry/profileBar.d.ts.map +1 -1
  147. package/dist/telemetry/profileFacts.d.ts +17 -0
  148. package/dist/telemetry/profileFacts.d.ts.map +1 -0
  149. package/dist/telemetry/profileOrder.d.ts +68 -0
  150. package/dist/telemetry/profileOrder.d.ts.map +1 -0
  151. package/dist/telemetry/profilePage.d.ts.map +1 -1
  152. package/dist/telemetry/profileSort.d.ts +35 -0
  153. package/dist/telemetry/profileSort.d.ts.map +1 -0
  154. package/dist/telemetry/profileSpent.d.ts +68 -0
  155. package/dist/telemetry/profileSpent.d.ts.map +1 -0
  156. package/dist/telemetry/providerPage.d.ts +2 -0
  157. package/dist/telemetry/providerPage.d.ts.map +1 -0
  158. package/dist/telemetry/providerSetup.d.ts +5 -0
  159. package/dist/telemetry/providerSetup.d.ts.map +1 -0
  160. package/dist/telemetry/providerView.d.ts +45 -0
  161. package/dist/telemetry/providerView.d.ts.map +1 -0
  162. package/dist/telemetry/routeChain.d.ts +32 -0
  163. package/dist/telemetry/routeChain.d.ts.map +1 -0
  164. package/dist/telemetry/routes.d.ts.map +1 -1
  165. package/dist/telemetry/settingsPage.d.ts +1 -1
  166. package/dist/telemetry/settingsPage.d.ts.map +1 -1
  167. package/dist/telemetry/sqlite.d.ts.map +1 -1
  168. package/dist/telemetry/store.d.ts +6 -1
  169. package/dist/telemetry/store.d.ts.map +1 -1
  170. package/dist/telemetry/types.d.ts +132 -2
  171. package/dist/telemetry/types.d.ts.map +1 -1
  172. package/dist/{tokenRefresh-kzz08kea.js → tokenRefresh-eg9h83wd.js} +6 -1
  173. package/package.json +11 -4
  174. package/dist/cli-5jxyma6z.js +0 -277
  175. package/dist/cli-vj9cv18n.js +0 -38
  176. package/dist/proxy/settings.d.ts +0 -30
  177. package/dist/proxy/settings.d.ts.map +0 -1
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The organization an account belongs to, as Anthropic names it.
3
+ *
4
+ * `GET /api/oauth/profile` answers `organization.name` — the human label for
5
+ * the account behind a profile. A fleet whose profiles are called `work2` and
6
+ * `corp3` is told apart by that label and by nothing else on the card.
7
+ *
8
+ * WHERE IT IS KEPT. `.credentials.json` already holds `subscriptionType` and
9
+ * `rateLimitTier`, so it looks like the natural home — but those are Claude
10
+ * Code's OWN keys. Meridian writes that file to be indistinguishable from one
11
+ * `claude login` wrote, and a Meridian-invented key breaks that deliberately;
12
+ * worse, Claude Code rewrites the file on its next refresh and would drop the
13
+ * key, so it would not even survive. This is Meridian's own file, beside
14
+ * settings.json, and neither objection applies to it.
15
+ *
16
+ * WHY IT IS CACHED. The render path must not make an authenticated round trip
17
+ * per profile: two pages poll `/profiles/list` every 10s and a fleet is a dozen
18
+ * accounts, which is a dozen requests to Anthropic every ten seconds for a
19
+ * string that changes approximately never. So reads are synchronous and
20
+ * cache-only, and a stale entry is refreshed in the background — the response
21
+ * never waits on the network.
22
+ *
23
+ * Leaf module — no imports from server.ts or session/.
24
+ */
25
+ import { type CredentialStore } from "./tokenRefresh";
26
+ /** How old an entry may get before a background refresh is worth making. */
27
+ export declare const ORGANIZATION_REFRESH_AFTER_MS: number;
28
+ /** Floor between two lookups for one profile, successful or not, so an
29
+ * unreachable Anthropic cannot be retried once per 10s poll forever. */
30
+ export declare const ORGANIZATION_RETRY_AFTER_MS: number;
31
+ export interface OrganizationRecord {
32
+ name: string;
33
+ fetchedAt: number;
34
+ }
35
+ /**
36
+ * Read a record map out of whatever the file actually holds. Hand-editable and
37
+ * written by other versions, so an entry that is not a usable name is dropped
38
+ * rather than served.
39
+ */
40
+ export declare function readOrganizations(raw: unknown): Record<string, OrganizationRecord>;
41
+ /**
42
+ * Pull the name out of an `/api/oauth/profile` body.
43
+ *
44
+ * Nullable in principle even though every account measured has one, so an
45
+ * absent name yields null and the caller renders nothing rather than "unknown".
46
+ */
47
+ export declare function extractOrganizationName(body: unknown): string | null;
48
+ export declare function organizationNeedsRefresh(record: OrganizationRecord | undefined, now: number, refreshAfterMs?: number): boolean;
49
+ /** Every organization on record, keyed by profile id. */
50
+ export declare function organizationNames(): Record<string, OrganizationRecord>;
51
+ export declare function rememberOrganizationName(profileId: string, name: string): void;
52
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
53
+ /**
54
+ * Best-effort lookup for one account. Never throws and never fails its caller:
55
+ * a profile whose organization is unknown is strictly better than a page that
56
+ * will not render.
57
+ */
58
+ export declare function fetchOrganizationName(store: CredentialStore, fetchImpl?: FetchLike): Promise<string | null>;
59
+ export declare function enableOrganizationLookup(): void;
60
+ export interface RefreshOrganizationOpts {
61
+ claudeConfigDir?: string;
62
+ store?: CredentialStore;
63
+ fetchImpl?: FetchLike;
64
+ retryAfterMs?: number;
65
+ }
66
+ /** Learn one profile's organization in the background. Returns immediately. */
67
+ export declare function refreshOrganizationNameSoon(profileId: string, opts?: RefreshOrganizationOpts): void;
68
+ /** Reset module state — for testing only. */
69
+ export declare function resetOrganizationLookupForTesting(): void;
70
+ export {};
71
+ //# sourceMappingURL=organizationName.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"organizationName.d.ts","sourceRoot":"","sources":["../../src/proxy/organizationName.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,OAAO,EAAiC,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAIpF,4EAA4E;AAC5E,eAAO,MAAM,6BAA6B,QAAsB,CAAA;AAEhE;yEACyE;AACzE,eAAO,MAAM,2BAA2B,QAAgB,CAAA;AAcxD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;CAClB;AAID;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAalF;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CASpE;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,kBAAkB,GAAG,SAAS,EACtC,GAAG,EAAE,MAAM,EACX,cAAc,GAAE,MAAsC,GACrD,OAAO,CAGT;AAID,yDAAyD;AACzD,wBAAgB,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAQtE;AAED,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAS9E;AAID,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEzE;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,eAAe,EACtB,SAAS,GAAE,SAA4B,GACtC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsBxB;AAWD,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAKD,MAAM,WAAW,uBAAuB;IACtC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,+EAA+E;AAC/E,wBAAgB,2BAA2B,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,GAAE,uBAA4B,GAAG,IAAI,CAsBvG;AAED,6CAA6C;AAC7C,wBAAgB,iCAAiC,IAAI,IAAI,CAIxD"}
@@ -113,6 +113,16 @@ export interface CompleteToolResultContinuationOptions {
113
113
  */
114
114
  allowTrailingSystemReminder?: boolean;
115
115
  }
116
+ /**
117
+ * Detect mid-conversation effort metadata system messages (e.g. pi >= 0.85.0
118
+ * Persistent Claude thinking effort). These carry effort metadata in
119
+ * `output_config` with empty content and no tool ids or text instructions.
120
+ */
121
+ export declare function isMidConvoEffortSystemMessage(message: {
122
+ role?: unknown;
123
+ content?: unknown;
124
+ output_config?: unknown;
125
+ } | null | undefined): boolean;
116
126
  /**
117
127
  * Verify that a resumed tool-result delta settles exactly the tool calls at the
118
128
  * stored assistant checkpoint, then coalesce queued user turns into one SDK
@@ -1 +1 @@
1
- {"version":3,"file":"passthroughEarlyStop.d.ts","sourceRoot":"","sources":["../../src/proxy/passthroughEarlyStop.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"passthroughEarlyStop.d.ts","sourceRoot":"","sources":["../../src/proxy/passthroughEarlyStop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Plan → coding-allotment multiplier.
3
+ *
4
+ * Anthropic sizes a plan's Claude Code allotment as a multiple of the Pro
5
+ * baseline — 5x, 20x, 6.25x — and that number, not the plan name, is what says
6
+ * how much work an account can actually do. Two accounts both reporting
7
+ * `Plan: max` can differ by 4x in capacity, so a page that shows only the plan
8
+ * cannot tell the user which account has room left.
9
+ *
10
+ * The multiplier is derived from the raw strings the profile endpoint returns
11
+ * — `seatTier` for a Team account, `rateLimitTier` otherwise — rather than
12
+ * from `subscriptionType`, because that field is too coarse: it collapses Max
13
+ * 5x and Max 20x into the single word `max`, and Team Standard and Team
14
+ * Premium into `team`. `subscriptionType` names the family and is used for the
15
+ * size only as a fallback for the one tier it pins unambiguously.
16
+ *
17
+ * An unrecognized tier yields null rather than a default, which is the whole
18
+ * point of the field: rendering `1x` for an account that is really 20x is
19
+ * worse than rendering nothing, because a reader acts on it.
20
+ *
21
+ * This is a leaf module — pure, no I/O, no imports.
22
+ */
23
+ export interface PlanAllowance {
24
+ /** Multiplier as displayed, e.g. `"20x"`. Null when the tier is unknown. */
25
+ multiplier: string | null;
26
+ /**
27
+ * The same value as a number, so callers can weight or rank accounts without
28
+ * re-parsing the label. Null whenever `multiplier` is.
29
+ */
30
+ weight: number | null;
31
+ /** Tier label, e.g. `"Personal Max"`. Null when the tier is unknown. */
32
+ label: string | null;
33
+ /**
34
+ * Which side of Anthropic's pricing the account sits on — `"Personal"`,
35
+ * `"Team"` or `"Enterprise"`. Separate from `planName` because the two are
36
+ * answered by different fields and either can be known without the other: an
37
+ * account whose family is declared but whose tier is unrecognized still has
38
+ * a trustworthy family.
39
+ */
40
+ accountType: string | null;
41
+ /**
42
+ * The plan WITHIN that family, in the vocabulary of Anthropic's own pricing
43
+ * page: `"Max 20x"`, `"Max 5x"`, `"Pro"`, `"Free"` for personal accounts and
44
+ * `"Premium seat"` / `"Standard seat"` for Team ones.
45
+ *
46
+ * Null when the family is known but the plan is not — which is the common
47
+ * case for a Team seat with no `seat_tier`, since its `rate_limit_tier`
48
+ * reports the underlying bucket rather than the seat.
49
+ */
50
+ planName: string | null;
51
+ }
52
+ /**
53
+ * Reduce a raw tier string to the form the table below is keyed by.
54
+ *
55
+ * Anthropic prefixes the wire value (`default_claude_max_20x`) and separates
56
+ * words with underscores; the same tier also appears bare (`max_20x`) in
57
+ * places, and `team_tier_1` is the wire spelling of what the pricing page
58
+ * calls Team Premium. Normalizing first means one entry per tier instead of
59
+ * one per spelling.
60
+ */
61
+ export declare function normalizeRateLimitTier(raw: string | null | undefined): string | null;
62
+ /**
63
+ * Derive the allowance from whatever plan fields a credential file carries.
64
+ *
65
+ * Both arguments are optional because both are optional on disk: a profile
66
+ * written before the plan was persisted has neither, and one written by an
67
+ * older Meridian has only `subscriptionType`. Every unknown case returns the
68
+ * same all-null shape, so callers can render conditionally on one field.
69
+ */
70
+ export declare function planAllowance(fields: {
71
+ rateLimitTier?: string | null;
72
+ seatTier?: string | null;
73
+ subscriptionType?: string | null;
74
+ } | null | undefined): PlanAllowance;
75
+ //# sourceMappingURL=planAllowance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planAllowance.d.ts","sourceRoot":"","sources":["../../src/proxy/planAllowance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB;;;OAGG;IACH,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,wEAAwE;IACxE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B;;;;;;;;OAQG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AA4BD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CASpF;AAwED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE;IACpC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC,GAAG,IAAI,GAAG,SAAS,GAAG,aAAa,CAkBnC"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Liveness and readiness as two DIFFERENT questions, because the existing
3
+ * `/health` answers a third one and is wrong when used as either.
4
+ *
5
+ * `/health` verifies the ACTIVE profile's auth: it spawns `claude auth status`
6
+ * and returns 503 when that profile is logged out. As a liveness probe that is
7
+ * a restart loop waiting to happen - a logged-out account is not fixed by
8
+ * killing the process, and a supervisor pointed at it will keep killing a
9
+ * perfectly healthy proxy until somebody re-authenticates. As a readiness probe
10
+ * it is wrong in the other direction: it inspects one profile out of however
11
+ * many are configured, and its own catch-all answers `degraded` with status
12
+ * **200**, so an instance that cannot verify auth at all reads as ready.
13
+ *
14
+ * So:
15
+ *
16
+ * /livez is this PROCESS still turning - would a restart help?
17
+ * /readyz should traffic be sent to THIS instance rather than another?
18
+ *
19
+ * Named after kube-apiserver's own endpoints, and shaped like them - plain
20
+ * text `ok`, with `?verbose` listing every check - so a Kubernetes probe, a
21
+ * Caddy `health_uri` and a person with curl all read the same thing.
22
+ */
23
+ export type ProbeKind = "livez" | "readyz";
24
+ export interface ProbeCheck {
25
+ readonly name: string;
26
+ readonly ok: boolean;
27
+ /** Why it failed. Absent when it passed. */
28
+ readonly detail?: string;
29
+ }
30
+ export interface ProbeReport {
31
+ readonly ok: boolean;
32
+ readonly checks: readonly ProbeCheck[];
33
+ }
34
+ /**
35
+ * Liveness passes unconditionally, and that is the design rather than a stub.
36
+ *
37
+ * Reaching this function at all means the event loop is turning and the HTTP
38
+ * server is answering, which is the entire question. Every dependency added
39
+ * here - a subprocess, a credential file, a reachable upstream - is another
40
+ * way for this process to be killed over somebody else's outage, and a
41
+ * liveness probe that restarts a working process is strictly worse than no
42
+ * liveness probe at all.
43
+ */
44
+ export declare function livenessReport(): ProbeReport;
45
+ export interface ReadinessInput {
46
+ /** Profiles this instance would actually serve from. */
47
+ readonly profileCount: number;
48
+ /** Whether a Claude executable resolves for this instance. */
49
+ readonly claudeExecutableResolved: boolean;
50
+ }
51
+ /**
52
+ * Readiness admits only checks that are PER-INSTANCE.
53
+ *
54
+ * The point of readiness is to move traffic somewhere better, so a check that
55
+ * two instances fail together cannot move it anywhere - it can only take every
56
+ * instance out at once and turn a clear error into a 502. That is why the
57
+ * credentials are deliberately not checked here even though a proxy with no
58
+ * usable token serves nothing: instances share those files, so an outage of
59
+ * them is not a reason to prefer one instance over another, and the honest
60
+ * answer to the caller is the provider's own 401 rather than a gateway error.
61
+ *
62
+ * What remains is genuinely local to one process:
63
+ *
64
+ * profiles an instance whose configuration and whose
65
+ * `profiles.json` are both empty has no account to serve
66
+ * from, while a neighbour reading a populated one is
67
+ * fine. In a container that is entirely per-instance: the
68
+ * file lives under that container's own HOME, so a
69
+ * missing mount takes exactly one replica out.
70
+ * claude-executable resolved from `MERIDIAN_CLAUDE_PATH`, this install's
71
+ * own `node_modules`, or its platform package - all
72
+ * per-instance, and without one no request can be served
73
+ * (#478).
74
+ */
75
+ export declare function readinessReport(input: ReadinessInput): ProbeReport;
76
+ /**
77
+ * kube-apiserver's own output shape: `[+]name ok` per check, a verdict line,
78
+ * and a bare `ok` when everything passed and nobody asked for detail.
79
+ *
80
+ * The terse form is the one a load balancer polls every few seconds, so it
81
+ * stays two bytes; the failing form names the checks that failed even without
82
+ * `?verbose`, because a 503 nobody can explain is a 503 somebody disables.
83
+ */
84
+ export declare function renderProbe(kind: ProbeKind, report: ProbeReport, verbose: boolean): string;
85
+ //# sourceMappingURL=probes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probes.d.ts","sourceRoot":"","sources":["../../src/proxy/probes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAA;AAE1C,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAA;IACpB,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAA;CACvC;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAE5C;AAED,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,8DAA8D;IAC9D,QAAQ,CAAC,wBAAwB,EAAE,OAAO,CAAA;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,CAgBlE;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAS1F"}
@@ -1,17 +1,27 @@
1
1
  /**
2
2
  * CLI commands for profile management.
3
3
  *
4
- * Browser-login profiles are stored under ~/.config/meridian/profiles/{id}/
4
+ * Browser-login profiles are stored under <config dir>/profiles/{id}/
5
5
  * — each directory is a standalone CLAUDE_CONFIG_DIR with its own OAuth
6
6
  * tokens. OAuth-token profiles (added via `--oauth-token`) live entirely in
7
7
  * profiles.json — no per-profile config dir.
8
8
  *
9
9
  * This is a leaf module — no imports from server.ts or session/.
10
10
  */
11
+ import { type OAuthPlanFields } from "./oauthPlan";
11
12
  import type { ProfileConfig } from "./profiles";
13
+ import { type CredentialsFile } from "./tokenRefresh";
12
14
  export declare const OAUTH_TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
13
15
  export declare const OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
14
16
  export declare const OAUTH_REDIRECT_URI = "https://platform.claude.com/oauth/code/callback";
17
+ /**
18
+ * Pure: is this ID safe to use as a profile name?
19
+ *
20
+ * A profile ID becomes a directory name under PROFILES_DIR, so anything
21
+ * carrying a path separator or a `..` segment would escape that directory.
22
+ * Restricting to plain identifiers keeps IDs both safe and shell-friendly.
23
+ */
24
+ export declare function isValidProfileId(id: string): boolean;
15
25
  interface AuthLoginOptions {
16
26
  headless?: boolean;
17
27
  }
@@ -24,9 +34,29 @@ interface ParsedAuthorizationCode {
24
34
  code: string;
25
35
  state?: string;
26
36
  }
37
+ interface OAuthTokenResponse {
38
+ access_token: string;
39
+ refresh_token?: string;
40
+ expires_in?: number;
41
+ expires_at?: number;
42
+ scope?: string;
43
+ }
27
44
  export declare function buildAuthLoginEnv(configDir: string | undefined, _options?: AuthLoginOptions, baseEnv?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
28
45
  export declare function createManualOAuthSession(scopes?: string[]): ManualOAuthSession;
29
46
  export declare function parseAuthorizationCodeInput(input: string): ParsedAuthorizationCode | null;
47
+ type CompleteOAuthTokenResponse = OAuthTokenResponse & {
48
+ refresh_token: string;
49
+ };
50
+ /**
51
+ * Build the record a login writes to disk.
52
+ *
53
+ * Split out so the on-disk shape is directly assertable: the defect this closes
54
+ * was a field missing from this object literal, which no test of the
55
+ * surrounding prompt/fetch I/O would have caught. Plan fields spread in only
56
+ * when known — `subscriptionType: undefined` would write a null-ish key into a
57
+ * file the real CLI also parses.
58
+ */
59
+ export declare function buildLoginCredentials(tokenData: CompleteOAuthTokenResponse, plan: OAuthPlanFields, now?: number): CredentialsFile;
30
60
  export declare function profileAdd(id: string, options?: AuthLoginOptions): Promise<void>;
31
61
  export declare function profileAddOauthToken(id: string, tokenArg: string | undefined): Promise<void>;
32
62
  export declare function profileList(): void;
@@ -41,7 +71,29 @@ export declare function profileList(): void;
41
71
  */
42
72
  export declare function dirsToRemoveOnProfileRemove(profile: ProfileConfig, profilesDir: string): string[];
43
73
  export declare function profileRemove(id: string): void;
74
+ export declare function profileRename(from: string, to: string): void;
44
75
  export declare function profileSwitch(id: string): Promise<void>;
76
+ export type ProfileLoginPlan = {
77
+ action: "create";
78
+ } | {
79
+ action: "reject-invalid-id";
80
+ } | {
81
+ action: "reject-oauth-token";
82
+ } | {
83
+ action: "login";
84
+ profile: ProfileConfig;
85
+ };
86
+ /**
87
+ * Pure: decide what `meridian profile login <id>` should do.
88
+ *
89
+ * An unknown ID is a request to create that profile, not an error — the ID is
90
+ * validated first because only this branch turns it into a directory. A profile
91
+ * that already exists is never re-validated: it was written by an earlier `add`
92
+ * (or by hand), and login must keep working on it either way.
93
+ *
94
+ * Caller performs the login itself — this returns the decision only.
95
+ */
96
+ export declare function planProfileLogin(id: string, profiles: ProfileConfig[]): ProfileLoginPlan;
45
97
  export declare function profileLogin(id: string, options?: AuthLoginOptions): Promise<void>;
46
98
  export declare function profileHelp(): void;
47
99
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"profileCli.d.ts","sourceRoot":"","sources":["../../src/proxy/profileCli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAO/C,eAAO,MAAM,eAAe,+CAA+C,CAAA;AAC3E,eAAO,MAAM,eAAe,yCAAyC,CAAA;AACrE,eAAO,MAAM,kBAAkB,oDAAoD,CAAA;AAkBnF,UAAU,gBAAgB;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED,UAAU,kBAAkB;IAC1B,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,UAAU,uBAAuB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAUD,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,GAAE,gBAAqB,EAC/B,OAAO,GAAE,MAAM,CAAC,UAAwB,GACvC,MAAM,CAAC,UAAU,CAInB;AAMD,wBAAgB,wBAAwB,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,kBAAkB,CAe9E;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,MAAM,GAAG,uBAAuB,GAAG,IAAI,CAiBzF;AAiHD,wBAAsB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwG1F;AAED,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BlG;AAED,wBAAgB,WAAW,IAAI,IAAI,CAsBlC;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAUjG;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAyB9C;AAED,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB7D;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkD5F;AAoED,wBAAgB,WAAW,IAAI,IAAI,CAsBlC"}
1
+ {"version":3,"file":"profileCli.d.ts","sourceRoot":"","sources":["../../src/proxy/profileCli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * What we know, right now, about accounts that have just been refused.
3
+ *
4
+ * Two stores, both in-memory and both deliberately not persisted - this is
5
+ * observability and routing hygiene, not durable truth. After a restart the
6
+ * next refusal re-establishes everything.
7
+ *
8
+ * `SpentStore` holds the latest refusal per profile, so a UI can say "this
9
+ * account is refusing" alongside the cached percentages instead of replacing
10
+ * them. Measured on the live fleet: an account rendered `5h 67% / 7d 7%` while
11
+ * every request through it was being refused, because those percentages are a
12
+ * snapshot of the last successful read and a refusal never touched them.
13
+ *
14
+ * `FailoverEventLog` is a bounded, monotonically numbered event ring so an
15
+ * external supervisor (vibeterm's account switcher) can poll for refusals and
16
+ * failovers. It exists because `claudeLog("profile.failover", …)` went to the
17
+ * log and nowhere else, and because `/telemetry/requests` - the obvious place
18
+ * to look - is a 500-row ring with no cursor, so a poller cannot tell "nothing
19
+ * happened" from "it scrolled past between polls". The `since` cursor here is
20
+ * the fix for exactly that.
21
+ *
22
+ * Leaf module: no I/O, no imports from server.ts. Clocks are injectable so the
23
+ * tests do not depend on wall time.
24
+ */
25
+ import type { LimitDiagnosis } from "./limitDetection";
26
+ export interface SpentRecord {
27
+ profileId: string;
28
+ /** When the refusal was observed (epoch ms). */
29
+ at: number;
30
+ diagnosis: LimitDiagnosis;
31
+ /** When this account is expected to serve again, when known. */
32
+ until: number | null;
33
+ /** Truncated raw SDK message - the operator's evidence for the verdict. */
34
+ message: string;
35
+ }
36
+ /**
37
+ * Latest refusal per profile, dropped on read once it expires (the same
38
+ * read-time expiry `ProfileExhaustion` uses, so the two never disagree about
39
+ * whether an account is currently in trouble).
40
+ */
41
+ export declare class SpentStore {
42
+ private readonly now;
43
+ private readonly records;
44
+ constructor(now?: () => number);
45
+ /**
46
+ * Record a refusal. A later record always replaces an earlier one: unlike an
47
+ * exhaustion mark (where a longer cooldown must win), the newest refusal is
48
+ * by definition the best description of the account's current state.
49
+ */
50
+ record(profileId: string, diagnosis: LimitDiagnosis, message: string): SpentRecord;
51
+ get(profileId: string): SpentRecord | undefined;
52
+ /** Live records only - expired ones are dropped on read. */
53
+ snapshot(): SpentRecord[];
54
+ clear(): void;
55
+ }
56
+ /**
57
+ * `refused` - an account said no. Emitted in every routing mode, so an
58
+ * exhausted account is visible even when nothing failed over.
59
+ * `failover` - a request was re-issued on another account and succeeded,
60
+ * so the client never saw the refusal.
61
+ * `pool_exhausted` - every candidate refused; the client got the error.
62
+ */
63
+ export type FailoverEventKind = "refused" | "failover" | "pool_exhausted";
64
+ export interface FailoverEvent {
65
+ /** Monotonic, starts at 1, never reused. The `since` cursor. */
66
+ seq: number;
67
+ at: number;
68
+ kind: FailoverEventKind;
69
+ /** The account that refused. */
70
+ profile: string;
71
+ /** The account that served the request instead, for `failover`. */
72
+ servedBy: string | null;
73
+ reason: string;
74
+ /** Routing mode in effect when this happened. */
75
+ routing: string;
76
+ sessionKey: string | null;
77
+ /**
78
+ * True when this refusal happened on an internal re-proxy hop, meaning the
79
+ * client did not see it. A `refused` event with `internalHop: false` is one
80
+ * the connected agent actually received.
81
+ */
82
+ internalHop: boolean;
83
+ until: number | null;
84
+ limit: LimitDiagnosis | null;
85
+ }
86
+ export interface FailoverEventPage {
87
+ events: FailoverEvent[];
88
+ /**
89
+ * Cursor for the next poll. The seq of the LAST RETURNED event, or the
90
+ * requested `since` when nothing was returned - never the ring's newest seq,
91
+ * because a page truncated by `limit` would then skip the remainder.
92
+ */
93
+ nextSince: number;
94
+ oldestSeq: number;
95
+ latestSeq: number;
96
+ /**
97
+ * True when events between the requested `since` and the oldest retained one
98
+ * were evicted, so the caller knows it missed some rather than believing it
99
+ * has seen everything.
100
+ */
101
+ dropped: boolean;
102
+ capacity: number;
103
+ }
104
+ /**
105
+ * Bounded ring of refusal/failover events, oldest-first on read.
106
+ *
107
+ * Capacity is generous relative to the event rate (one per refusal, not one per
108
+ * request), so a consumer polling every few seconds cannot outrun it; `dropped`
109
+ * exists for the case where a consumer was away long enough that it did.
110
+ */
111
+ export declare class FailoverEventLog {
112
+ private readonly capacity;
113
+ private readonly now;
114
+ private readonly events;
115
+ private seq;
116
+ constructor(capacity?: number, now?: () => number);
117
+ append(event: Omit<FailoverEvent, "seq" | "at"> & {
118
+ at?: number;
119
+ }): FailoverEvent;
120
+ /**
121
+ * Events strictly after `since`, oldest-first. `since = 0` returns everything
122
+ * retained, which is what a consumer starting cold wants.
123
+ */
124
+ since(since: number, limit?: number): FailoverEventPage;
125
+ clear(): void;
126
+ }
127
+ //# sourceMappingURL=profileHealth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profileHealth.d.ts","sourceRoot":"","sources":["../../src/proxy/profileHealth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEtD,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,gDAAgD;IAChD,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,cAAc,CAAA;IACzB,gEAAgE;IAChE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAA;CAChB;AAWD;;;;GAIG;AACH,qBAAa,UAAU;IAGT,OAAO,CAAC,QAAQ,CAAC,GAAG;IAFhC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;gBAE5B,GAAG,GAAE,MAAM,MAAiB;IAEzD;;;;OAIG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW;IAiBlF,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAU/C,4DAA4D;IAC5D,QAAQ,IAAI,WAAW,EAAE;IASzB,KAAK,IAAI,IAAI;CAGd;AAED;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,UAAU,GAAG,gBAAgB,CAAA;AAEzE,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,GAAG,EAAE,MAAM,CAAA;IACX,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,iBAAiB,CAAA;IACvB,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,mEAAmE;IACnE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB;;;;OAIG;IACH,WAAW,EAAE,OAAO,CAAA;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,KAAK,EAAE,cAAc,GAAG,IAAI,CAAA;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAKD;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAKzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG;IALtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;IAC7C,OAAO,CAAC,GAAG,CAAI;gBAGI,QAAQ,GAAE,MAA+B,EACzC,GAAG,GAAE,MAAM,MAAiB;IAG/C,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,KAAK,GAAG,IAAI,CAAC,GAAG;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,aAAa;IAOjF;;;OAGG;IACH,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAA2B,GAAG,iBAAiB;IAkB3E,KAAK,IAAI,IAAI;CAId"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Profile renaming.
3
+ *
4
+ * A rename has to move everything that names a profile: its entry in
5
+ * profiles.json and, when its credentials live under
6
+ * ~/.config/meridian/profiles/<id>, the credential directory itself. Renaming
7
+ * the entry alone would leave the profile pointing at a directory that is no
8
+ * longer its own — an account logged out by a cosmetic change — so both are
9
+ * planned together and applied as one step that either happens or does not.
10
+ *
11
+ * The old name survives as an ALIAS on the renamed profile. It is a redirect,
12
+ * not a second name: after `x` is renamed to `y`, `x-meridian-profile: x` is
13
+ * served by `y`, and the moment `x` is claimed again — by `profile add x`, or
14
+ * by renaming something else to `x` — the redirect is dropped.
15
+ *
16
+ * Chains are collapsed on write. Renaming x→y and later y→z leaves z holding
17
+ * both "x" and "y", so resolution is a single lookup and no alias can dangle
18
+ * at an intermediate name that no longer exists.
19
+ *
20
+ * This is a leaf module — no imports from server.ts or session/.
21
+ */
22
+ import type { ProfileConfig } from "./profiles";
23
+ export declare function isValidProfileId(id: string): boolean;
24
+ export declare function defaultProfilesDir(): string;
25
+ export declare function defaultProfilesConfigFile(): string;
26
+ export declare function loadProfileConfigFrom(file: string): ProfileConfig[];
27
+ export declare function saveProfileConfigTo(file: string, profiles: ProfileConfig[]): void;
28
+ /**
29
+ * Drop `name` from every profile's alias list — a real profile is claiming the
30
+ * name, so the redirect has to stop. Profiles with nothing to drop are
31
+ * returned as-is.
32
+ */
33
+ export declare function reclaimAlias(profiles: ProfileConfig[], name: string): ProfileConfig[];
34
+ export interface ProfileRenamePlan {
35
+ /** The complete profile list as it should be written to disk. */
36
+ profiles: ProfileConfig[];
37
+ /** Credential directory to move, when this profile keeps one under profilesDir. */
38
+ dirMove?: {
39
+ from: string;
40
+ to: string;
41
+ };
42
+ /** Aliases the renamed profile ends up carrying, chains already collapsed. */
43
+ aliases: string[];
44
+ }
45
+ export type ProfileRenameResult = {
46
+ ok: true;
47
+ plan: ProfileRenamePlan;
48
+ } | {
49
+ ok: false;
50
+ error: string;
51
+ hint?: string;
52
+ };
53
+ /**
54
+ * Pure: decide what a rename would change. Refuses rather than corrupting —
55
+ * an unknown source, an occupied target, or a name the charset rejects each
56
+ * come back as an error with nothing planned.
57
+ */
58
+ export declare function planProfileRename(profiles: ProfileConfig[], from: string, to: string, profilesDir: string): ProfileRenameResult;
59
+ export interface ApplyProfileRenameOptions {
60
+ /** Directory holding per-profile credential dirs. Defaults to the real one. */
61
+ profilesDir?: string;
62
+ /** profiles.json path. Defaults to the real one. */
63
+ configFile?: string;
64
+ }
65
+ export type ApplyProfileRenameResult = {
66
+ ok: true;
67
+ from: string;
68
+ to: string;
69
+ aliases: string[];
70
+ profiles: ProfileConfig[];
71
+ } | {
72
+ ok: false;
73
+ error: string;
74
+ hint?: string;
75
+ };
76
+ /**
77
+ * Apply a rename to disk: credentials first, then the profile list, then the
78
+ * active pointer. If the profile list cannot be written the credential move is
79
+ * undone, so the profile is never left naming a directory that moved out from
80
+ * under it.
81
+ */
82
+ export declare function applyProfileRename(from: string, to: string, options?: ApplyProfileRenameOptions): ApplyProfileRenameResult;
83
+ //# sourceMappingURL=profileRename.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profileRename.d.ts","sourceRoot":"","sources":["../../src/proxy/profileRename.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAK/C,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,yBAAyB,IAAI,MAAM,CAElD;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CASnE;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,IAAI,CAGjF;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CASrF;AAED,MAAM,WAAW,iBAAiB;IAChC,iEAAiE;IACjE,QAAQ,EAAE,aAAa,EAAE,CAAA;IACzB,mFAAmF;IACnF,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;IACtC,8EAA8E;IAC9E,OAAO,EAAE,MAAM,EAAE,CAAA;CAClB;AAED,MAAM,MAAM,mBAAmB,GAC3B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAA;CAAE,GACrC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/C;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,aAAa,EAAE,EACzB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,GAClB,mBAAmB,CA2CrB;AAED,MAAM,WAAW,yBAAyB;IACxC,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oDAAoD;IACpD,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,MAAM,wBAAwB,GAChC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,aAAa,EAAE,CAAA;CAAE,GACpF;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/C;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EACV,OAAO,GAAE,yBAA8B,GACtC,wBAAwB,CAwD1B"}