@rynfar/meridian 1.72.0 → 1.73.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/dist/cli-1jstkdj9.js +30 -0
- package/dist/{cli-9e5cxp89.js → cli-2h3w5n6g.js} +163 -65
- package/dist/cli-3zwfvexc.js +56 -0
- package/dist/{cli-hxxy0m1z.js → cli-82e2m44k.js} +2 -1
- package/dist/cli-8sw5wr9j.js +136 -0
- package/dist/{cli-khhjyk04.js → cli-chsp880k.js} +214 -21
- package/dist/cli-dh0a1bw4.js +619 -0
- package/dist/cli-eehk3052.js +57 -0
- package/dist/cli-grfp0s1j.js +330 -0
- package/dist/{cli-ab7zkp1a.js → cli-p5zbk952.js} +2027 -315
- package/dist/{cli-hj61zpt7.js → cli-p9ca4p13.js} +17 -2
- package/dist/cli-qpfky8wy.js +133 -0
- package/dist/cli.js +47 -12
- package/dist/cliDashboard-c0dvkj4y.js +325 -0
- package/dist/configDir.d.ts +30 -0
- package/dist/configDir.d.ts.map +1 -0
- package/dist/organizationName-90f0atkn.js +29 -0
- package/dist/{pluginPage-f5gdmbxc.js → pluginPage-3kxsrdpj.js} +1 -1
- package/dist/{profileCli-1eecbmgk.js → profileCli-1bfgc05h.js} +13 -3
- package/dist/{profilePage-mamej7kp.js → profilePage-etxqg3vn.js} +252 -60
- package/dist/profileRename-4a4km6g8.js +22 -0
- package/dist/{profiles-wch9h234.js → profiles-0mp32ek9.js} +16 -3
- package/dist/proxy/adapterInstances.d.ts +1 -1
- package/dist/proxy/adapterInstances.d.ts.map +1 -1
- package/dist/proxy/adapters/claudecode.d.ts.map +1 -1
- package/dist/proxy/adapters/droid.d.ts.map +1 -1
- package/dist/proxy/authDiscovery.d.ts +52 -0
- package/dist/proxy/authDiscovery.d.ts.map +1 -0
- package/dist/proxy/credentialsMode.d.ts +77 -0
- package/dist/proxy/credentialsMode.d.ts.map +1 -0
- package/dist/proxy/design.d.ts.map +1 -1
- package/dist/proxy/followActive.d.ts +262 -0
- package/dist/proxy/followActive.d.ts.map +1 -0
- package/dist/proxy/limitDetection.d.ts +110 -0
- package/dist/proxy/limitDetection.d.ts.map +1 -0
- package/dist/proxy/models.d.ts.map +1 -1
- package/dist/proxy/oauthPlan.d.ts +63 -0
- package/dist/proxy/oauthPlan.d.ts.map +1 -0
- package/dist/proxy/oauthUsage.d.ts +65 -2
- package/dist/proxy/oauthUsage.d.ts.map +1 -1
- package/dist/proxy/openai.d.ts +4 -2
- package/dist/proxy/openai.d.ts.map +1 -1
- package/dist/proxy/organizationName.d.ts +71 -0
- package/dist/proxy/organizationName.d.ts.map +1 -0
- package/dist/proxy/passthroughEarlyStop.d.ts +10 -0
- package/dist/proxy/passthroughEarlyStop.d.ts.map +1 -1
- package/dist/proxy/planAllowance.d.ts +75 -0
- package/dist/proxy/planAllowance.d.ts.map +1 -0
- package/dist/proxy/probes.d.ts +85 -0
- package/dist/proxy/probes.d.ts.map +1 -0
- package/dist/proxy/profileCli.d.ts +53 -1
- package/dist/proxy/profileCli.d.ts.map +1 -1
- package/dist/proxy/profileHealth.d.ts +127 -0
- package/dist/proxy/profileHealth.d.ts.map +1 -0
- package/dist/proxy/profileRename.d.ts +83 -0
- package/dist/proxy/profileRename.d.ts.map +1 -0
- package/dist/proxy/profiles.d.ts +72 -6
- package/dist/proxy/profiles.d.ts.map +1 -1
- package/dist/proxy/query.d.ts +7 -0
- package/dist/proxy/query.d.ts.map +1 -1
- package/dist/proxy/routing.d.ts +59 -1
- package/dist/proxy/routing.d.ts.map +1 -1
- package/dist/proxy/sdkFeatures.d.ts +1 -1
- package/dist/proxy/sdkFeatures.d.ts.map +1 -1
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/session/fingerprint.d.ts +5 -0
- package/dist/proxy/session/fingerprint.d.ts.map +1 -1
- package/dist/proxy/session/lineage.d.ts.map +1 -1
- package/dist/proxy/setup.d.ts.map +1 -1
- package/dist/proxy/supervision.d.ts +41 -0
- package/dist/proxy/supervision.d.ts.map +1 -0
- package/dist/proxy/tokenRefresh.d.ts +38 -4
- package/dist/proxy/tokenRefresh.d.ts.map +1 -1
- package/dist/server.js +11 -7
- package/dist/settings.d.ts +83 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/{setup-ndmjpy23.js → setup-7fr49z92.js} +1 -1
- package/dist/statusProbe-9x9q261e.js +151 -0
- package/dist/telemetry/dashboard.d.ts.map +1 -1
- package/dist/telemetry/index.d.ts +20 -2
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/landing.d.ts +1 -1
- package/dist/telemetry/landing.d.ts.map +1 -1
- package/dist/telemetry/logStore.d.ts +11 -0
- package/dist/telemetry/logStore.d.ts.map +1 -1
- package/dist/telemetry/pricingStore.d.ts +2 -2
- package/dist/telemetry/pricingStore.d.ts.map +1 -1
- package/dist/telemetry/profileBar.d.ts +2 -2
- package/dist/telemetry/profileBar.d.ts.map +1 -1
- package/dist/telemetry/profileFacts.d.ts +17 -0
- package/dist/telemetry/profileFacts.d.ts.map +1 -0
- package/dist/telemetry/profileOrder.d.ts +68 -0
- package/dist/telemetry/profileOrder.d.ts.map +1 -0
- package/dist/telemetry/profilePage.d.ts.map +1 -1
- package/dist/telemetry/profileSort.d.ts +35 -0
- package/dist/telemetry/profileSort.d.ts.map +1 -0
- package/dist/telemetry/profileSpent.d.ts +68 -0
- package/dist/telemetry/profileSpent.d.ts.map +1 -0
- package/dist/telemetry/routeChain.d.ts +32 -0
- package/dist/telemetry/routeChain.d.ts.map +1 -0
- package/dist/telemetry/routes.d.ts.map +1 -1
- package/dist/telemetry/settingsPage.d.ts +1 -1
- package/dist/telemetry/settingsPage.d.ts.map +1 -1
- package/dist/telemetry/sqlite.d.ts.map +1 -1
- package/dist/telemetry/store.d.ts +6 -1
- package/dist/telemetry/store.d.ts.map +1 -1
- package/dist/telemetry/types.d.ts +132 -2
- package/dist/telemetry/types.d.ts.map +1 -1
- package/dist/{tokenRefresh-kzz08kea.js → tokenRefresh-eg9h83wd.js} +6 -1
- package/package.json +1 -1
- package/dist/cli-5jxyma6z.js +0 -277
- package/dist/cli-vj9cv18n.js +0 -38
- package/dist/proxy/settings.d.ts +0 -30
- package/dist/proxy/settings.d.ts.map +0 -1
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How this proxy was started, so a page that needs a restart can say HOW.
|
|
3
|
+
*
|
|
4
|
+
* The telemetry stores are built once at startup, so changing where telemetry
|
|
5
|
+
* is kept takes effect on the next start and not before. A settings page that
|
|
6
|
+
* says "restart to apply" and stops there has handed the reader a puzzle: the
|
|
7
|
+
* unit is named by whoever installed it, `systemctl restart` without `--user`
|
|
8
|
+
* silently addresses a different manager, and a proxy started by hand has no
|
|
9
|
+
* unit at all. All of that is already written in /proc/self/cgroup.
|
|
10
|
+
*
|
|
11
|
+
* Linux-only by nature; everywhere else this reports `unknown` and the page
|
|
12
|
+
* falls back to "restart Meridian", which is the honest answer there.
|
|
13
|
+
*
|
|
14
|
+
* Leaf module: the parsing is pure and separately testable, and the single
|
|
15
|
+
* read is wrapped so a missing or unreadable cgroup file degrades to
|
|
16
|
+
* `unknown` rather than throwing during a settings fetch.
|
|
17
|
+
*/
|
|
18
|
+
export type SupervisionKind = "systemd-user" | "systemd-system" | "unknown";
|
|
19
|
+
export interface Supervision {
|
|
20
|
+
kind: SupervisionKind;
|
|
21
|
+
/** Unit name, e.g. "meridian-dev.service". Null when not under a unit. */
|
|
22
|
+
unit: string | null;
|
|
23
|
+
/** Exactly what to run to apply a restart-scoped change, or null when
|
|
24
|
+
* nothing can be named with confidence. */
|
|
25
|
+
restartCommand: string | null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Read the unit out of a cgroup file's contents.
|
|
29
|
+
*
|
|
30
|
+
* Handles both hierarchies: cgroup v2 writes one `0::/path` line, v1 writes
|
|
31
|
+
* several `id:controllers:/path` lines. Taking the substring after the last
|
|
32
|
+
* colon yields the path in either case.
|
|
33
|
+
*
|
|
34
|
+
* A `.scope` leaf (`session-2.scope`, `init.scope`) is deliberately NOT a
|
|
35
|
+
* match: a scope is how systemd accounts for a process it did not launch, so
|
|
36
|
+
* a proxy started by hand in a terminal lands in one. Reporting that as a unit
|
|
37
|
+
* would print a `systemctl restart session-2.scope` that fails.
|
|
38
|
+
*/
|
|
39
|
+
export declare function parseSupervision(cgroup: string): Supervision;
|
|
40
|
+
export declare function detectSupervision(): Supervision;
|
|
41
|
+
//# sourceMappingURL=supervision.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supervision.d.ts","sourceRoot":"","sources":["../../src/proxy/supervision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,gBAAgB,GAAG,SAAS,CAAA;AAE3E,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,eAAe,CAAA;IACrB,0EAA0E;IAC1E,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB;gDAC4C;IAC5C,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAQD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAsB5D;AAED,wBAAgB,iBAAiB,IAAI,WAAW,CAQ/C"}
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
export declare function configDirToKeychainService(claudeConfigDir: string): string;
|
|
26
26
|
/** Map `claudeConfigDir` to the file-based credentials path. */
|
|
27
27
|
export declare function configDirToCredentialsFile(claudeConfigDir: string): string;
|
|
28
|
-
interface OAuthCredentials {
|
|
28
|
+
export interface OAuthCredentials {
|
|
29
29
|
accessToken: string;
|
|
30
30
|
refreshToken: string;
|
|
31
31
|
expiresAt: number;
|
|
@@ -42,8 +42,16 @@ interface OAuthCredentials {
|
|
|
42
42
|
scopes?: string[];
|
|
43
43
|
subscriptionType?: string;
|
|
44
44
|
rateLimitTier?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Which seat a Team member holds. The only field that separates a Premium
|
|
47
|
+
* seat from a Standard one: measured across twelve live accounts, every
|
|
48
|
+
* Premium seat reports `rate_limit_tier: "default_claude_max_5x"` — what a
|
|
49
|
+
* personal Max 5x reports — and a Standard seat reports `default_raven`,
|
|
50
|
+
* which names no published allotment at all.
|
|
51
|
+
*/
|
|
52
|
+
seatTier?: string;
|
|
45
53
|
}
|
|
46
|
-
interface CredentialsFile {
|
|
54
|
+
export interface CredentialsFile {
|
|
47
55
|
claudeAiOauth: OAuthCredentials;
|
|
48
56
|
[key: string]: unknown;
|
|
49
57
|
}
|
|
@@ -77,6 +85,18 @@ export declare function createPlatformCredentialStore(opts?: {
|
|
|
77
85
|
}): CredentialStore;
|
|
78
86
|
/** Look up the appropriate file path for a profile (Linux convention even on macOS for inspection). */
|
|
79
87
|
export declare function credentialsFilePathForProfile(claudeConfigDir?: string): string;
|
|
88
|
+
/**
|
|
89
|
+
* What the stored credential says about this account's ability to authenticate.
|
|
90
|
+
*
|
|
91
|
+
* `unknown` is deliberately NOT a soft `absent`. `read()` answers `null` both
|
|
92
|
+
* for a credential that is not there and for one it could not parse, so from
|
|
93
|
+
* here the two are indistinguishable - and a Keychain that momentarily refuses,
|
|
94
|
+
* or a file caught mid-write, must never be allowed to report a working account
|
|
95
|
+
* as logged out. Only a credential that was read successfully and carries no
|
|
96
|
+
* access token is `absent`.
|
|
97
|
+
*/
|
|
98
|
+
export type StoredCredentialPresence = "present" | "absent" | "unknown";
|
|
99
|
+
export declare function readStoredCredentialPresence(store: CredentialStore): Promise<StoredCredentialPresence>;
|
|
80
100
|
/**
|
|
81
101
|
* Refresh the Claude Code OAuth access token.
|
|
82
102
|
*
|
|
@@ -132,8 +152,23 @@ export interface AuthRenewalStatus {
|
|
|
132
152
|
/** True once inside the warning window — the field monitors should alert on. */
|
|
133
153
|
renewalRequiredSoon: boolean;
|
|
134
154
|
}
|
|
135
|
-
/** Drop cached
|
|
155
|
+
/** Drop cached credential facts — for tests, and after a re-login. */
|
|
136
156
|
export declare function resetAuthRenewalCache(): void;
|
|
157
|
+
/**
|
|
158
|
+
* The plan fields a login persisted, as stored.
|
|
159
|
+
*
|
|
160
|
+
* `claude auth status` reports `subscriptionType` and nothing else, so the
|
|
161
|
+
* tier that separates Max 5x from Max 20x is only ever available here. Both
|
|
162
|
+
* fields are optional on disk — a credential file written before Meridian
|
|
163
|
+
* persisted them has neither — and absent keys are omitted rather than nulled
|
|
164
|
+
* so a caller can spread the result over a live status without erasing it.
|
|
165
|
+
*/
|
|
166
|
+
export interface StoredPlanFields {
|
|
167
|
+
subscriptionType?: string;
|
|
168
|
+
rateLimitTier?: string;
|
|
169
|
+
seatTier?: string;
|
|
170
|
+
}
|
|
171
|
+
export declare function getStoredPlanFields(store?: CredentialStore): Promise<StoredPlanFields>;
|
|
137
172
|
export declare function getAuthRenewalStatus(store?: CredentialStore, warnDays?: number): Promise<AuthRenewalStatus>;
|
|
138
173
|
/**
|
|
139
174
|
* Start a self-rescheduling timer that refreshes the access token shortly
|
|
@@ -163,5 +198,4 @@ export declare function stopBackgroundRefresh(): void;
|
|
|
163
198
|
export declare function isBackgroundRefreshActive(): boolean;
|
|
164
199
|
/** Reset in-flight state — for testing only. */
|
|
165
200
|
export declare function resetInflightRefresh(): void;
|
|
166
|
-
export {};
|
|
167
201
|
//# sourceMappingURL=tokenRefresh.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenRefresh.d.ts","sourceRoot":"","sources":["../../src/proxy/tokenRefresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;
|
|
1
|
+
{"version":3,"file":"tokenRefresh.d.ts","sourceRoot":"","sources":["../../src/proxy/tokenRefresh.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAoBH;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAK1E;AAED,gEAAgE;AAChE,wBAAgB,0BAA0B,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;;;;OAQG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,gBAAgB,CAAA;IAC/B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAMD,MAAM,WAAW,eAAe;IAC9B,kFAAkF;IAClF,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,IAAI,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAA;IACvC,KAAK,CAAC,WAAW,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACtD;AA+BD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,eAAe,GAAG,MAAM,CAEzE;AA4GD;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,eAAe,CAQlG;AAED,uGAAuG;AACvG,wBAAgB,6BAA6B,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAA;AAEvE,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,wBAAwB,CAAC,CAUnC;AAUD;;;;;;;;;;GAUG;AACH,wBAAsB,iBAAiB,CAAC,KAAK,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,CAmCjF;AAmHD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,CAAC,EAAE,eAAe,EACvB,QAAQ,SAAgB,GACvB,OAAO,CAAC,OAAO,CAAC,CAclB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,IAAI,CAAA;AAE1C;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAGtE;AAED,MAAM,WAAW,iBAAiB;IAChC,4DAA4D;IAC5D,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,gFAAgF;IAChF,mBAAmB,EAAE,OAAO,CAAA;CAC7B;AA2CD,sEAAsE;AACtE,wBAAgB,qBAAqB,IAAI,IAAI,CAG5C;AAiDD;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,wBAAsB,mBAAmB,CACvC,KAAK,CAAC,EAAE,eAAe,GACtB,OAAO,CAAC,gBAAgB,CAAC,CAQ3B;AAED,wBAAsB,oBAAoB,CACxC,KAAK,CAAC,EAAE,eAAe,EACvB,QAAQ,SAA4B,GACnC,OAAO,CAAC,iBAAiB,CAAC,CAkB5B;AAiBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,CAAC,EAAE,eAAe,EACvB,QAAQ,SAAgB,EACxB,cAAc,SAAgB,GAC7B,IAAI,CAUN;AAED,iDAAiD;AACjD,wBAAgB,qBAAqB,IAAI,IAAI,CAK5C;AAoED,wBAAwB;AACxB,wBAAgB,yBAAyB,IAAI,OAAO,CAEnD;AAED,gDAAgD;AAChD,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C"}
|
package/dist/server.js
CHANGED
|
@@ -11,14 +11,18 @@ import {
|
|
|
11
11
|
runObserveHook,
|
|
12
12
|
runTransformHook,
|
|
13
13
|
startProxyServer
|
|
14
|
-
} from "./cli-
|
|
15
|
-
import"./cli-
|
|
14
|
+
} from "./cli-p5zbk952.js";
|
|
15
|
+
import"./cli-82e2m44k.js";
|
|
16
|
+
import"./cli-dh0a1bw4.js";
|
|
17
|
+
import"./cli-8sw5wr9j.js";
|
|
18
|
+
import"./cli-grfp0s1j.js";
|
|
16
19
|
import"./cli-sry5aqdj.js";
|
|
17
|
-
import"./cli-
|
|
18
|
-
import"./cli-
|
|
19
|
-
import"./cli-
|
|
20
|
-
import"./cli-
|
|
21
|
-
import"./cli-
|
|
20
|
+
import"./cli-p9ca4p13.js";
|
|
21
|
+
import"./cli-2h3w5n6g.js";
|
|
22
|
+
import"./cli-chsp880k.js";
|
|
23
|
+
import"./cli-1jstkdj9.js";
|
|
24
|
+
import"./cli-qpfky8wy.js";
|
|
25
|
+
import"./cli-eehk3052.js";
|
|
22
26
|
import"./cli-p9swy5t3.js";
|
|
23
27
|
export {
|
|
24
28
|
startProxyServer,
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent server settings.
|
|
3
|
+
*
|
|
4
|
+
* Stored in ~/.config/meridian/settings.json (MERIDIAN_CONFIG_DIR moves the
|
|
5
|
+
* directory). Survives proxy restarts.
|
|
6
|
+
* Shared between CLI, UI, and API — browser localStorage is only used
|
|
7
|
+
* for client-only preferences (theme, collapsed sections, etc.).
|
|
8
|
+
*
|
|
9
|
+
* Lives at the src root, beside env.ts, rather than under proxy/, because
|
|
10
|
+
* telemetry reads it too and telemetry may not import from proxy —
|
|
11
|
+
* dependencies flow proxy → telemetry only. env.ts is the existing precedent
|
|
12
|
+
* for "configuration both layers need"; a copy of the path logic in each
|
|
13
|
+
* would be two things to keep agreeing about MERIDIAN_CONFIG_DIR.
|
|
14
|
+
*
|
|
15
|
+
* Leaf module: node builtins only, no imports from anywhere in the tree.
|
|
16
|
+
*/
|
|
17
|
+
import type { PriorityFailbackPolicy } from "./proxy/routing";
|
|
18
|
+
export interface MeridianSettings {
|
|
19
|
+
/** Last active profile ID — restored on proxy startup */
|
|
20
|
+
activeProfile?: string;
|
|
21
|
+
/** Profile routing mode (#383, priority spec): "active" (default),
|
|
22
|
+
* "sticky", or "priority". MERIDIAN_ROUTING env var takes precedence. */
|
|
23
|
+
routing?: string;
|
|
24
|
+
/** Priority-mode pool order (highest priority first). Falls back to
|
|
25
|
+
* profiles.json order. MERIDIAN_PROFILE_ORDER env var takes precedence. */
|
|
26
|
+
profileOrder?: string[];
|
|
27
|
+
priorityFailback?: PriorityFailbackPolicy;
|
|
28
|
+
/**
|
|
29
|
+
* Keep telemetry in SQLite instead of memory, so it survives a restart.
|
|
30
|
+
* MERIDIAN_TELEMETRY_PERSIST takes precedence.
|
|
31
|
+
*
|
|
32
|
+
* The four telemetry keys below differ from every other setting here in one
|
|
33
|
+
* way that the UI must not hide: the stores are built once at startup, so a
|
|
34
|
+
* change to any of them does nothing until the proxy is restarted. Routing
|
|
35
|
+
* is re-read per request; these are not, and cannot be without swapping a
|
|
36
|
+
* store out from under in-flight writes.
|
|
37
|
+
*/
|
|
38
|
+
telemetryPersist?: boolean;
|
|
39
|
+
/** Days before persisted telemetry is deleted. MERIDIAN_TELEMETRY_RETENTION_DAYS wins. */
|
|
40
|
+
telemetryRetentionDays?: number;
|
|
41
|
+
/** Rows the in-memory ring holds. Ignored when persisting. MERIDIAN_TELEMETRY_SIZE wins. */
|
|
42
|
+
telemetrySize?: number;
|
|
43
|
+
/** Entries the in-memory diagnostic log ring holds. MERIDIAN_DIAGNOSTIC_LOG_SIZE wins. */
|
|
44
|
+
diagnosticLogSize?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Accepted range for each numeric telemetry setting, and the reason for it.
|
|
48
|
+
*
|
|
49
|
+
* Lives here rather than in the route so the form and the validator read the
|
|
50
|
+
* same numbers: a page that offers a value the API rejects is a worse bug than
|
|
51
|
+
* either bound being slightly wrong.
|
|
52
|
+
*
|
|
53
|
+
* The upper bounds guard a value that only takes effect on the next start, so
|
|
54
|
+
* a typo is not discovered until a restart that then fails or thrashes. Both
|
|
55
|
+
* ring sizes are whole request/log records held in memory; a million rows is
|
|
56
|
+
* hundreds of megabytes of resident heap, which is not a setting anyone means
|
|
57
|
+
* to type. Retention is capped at ten years because SQLite cleanup deletes by
|
|
58
|
+
* a cutoff, and a cutoff further out than the data's usefulness simply never
|
|
59
|
+
* deletes anything.
|
|
60
|
+
*/
|
|
61
|
+
export declare const TELEMETRY_SETTING_LIMITS: {
|
|
62
|
+
readonly telemetryRetentionDays: {
|
|
63
|
+
readonly min: 1;
|
|
64
|
+
readonly max: 3650;
|
|
65
|
+
};
|
|
66
|
+
readonly telemetrySize: {
|
|
67
|
+
readonly min: 10;
|
|
68
|
+
readonly max: 1000000;
|
|
69
|
+
};
|
|
70
|
+
readonly diagnosticLogSize: {
|
|
71
|
+
readonly min: 10;
|
|
72
|
+
readonly max: 1000000;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
/** Read settings from disk. Returns empty object if file doesn't exist or is invalid. */
|
|
76
|
+
export declare function loadSettings(): MeridianSettings;
|
|
77
|
+
/** Write settings to disk. Merges with existing settings (doesn't clobber unknown keys). */
|
|
78
|
+
export declare function saveSettings(patch: Partial<MeridianSettings>): MeridianSettings;
|
|
79
|
+
/** Get a single setting value, or undefined if not set. */
|
|
80
|
+
export declare function getSetting<K extends keyof MeridianSettings>(key: K): MeridianSettings[K];
|
|
81
|
+
/** Update a single setting. */
|
|
82
|
+
export declare function setSetting<K extends keyof MeridianSettings>(key: K, value: MeridianSettings[K]): MeridianSettings;
|
|
83
|
+
//# sourceMappingURL=settings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAKH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAc7D,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;8EAC0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;gFAC4E;IAC5E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,gBAAgB,CAAC,EAAE,sBAAsB,CAAA;IAEzC;;;;;;;;;OASG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,0FAA0F;IAC1F,sBAAsB,CAAC,EAAE,MAAM,CAAA;IAC/B,4FAA4F;IAC5F,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,0FAA0F;IAC1F,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;CAI4B,CAAA;AAEjE,yFAAyF;AACzF,wBAAgB,YAAY,IAAI,gBAAgB,CAQ/C;AAED,4FAA4F;AAC5F,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,gBAAgB,CAO/E;AAED,2DAA2D;AAC3D,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,GAAG,EAAE,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAExF;AAED,+BAA+B;AAC/B,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAEjH"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import"./cli-p9swy5t3.js";
|
|
2
|
+
|
|
3
|
+
// src/proxy/statusProbe.ts
|
|
4
|
+
import { createServer } from "node:net";
|
|
5
|
+
var PROBE_TIMEOUT_MS = 2000;
|
|
6
|
+
function probeUrlBase(host, port) {
|
|
7
|
+
let target = host;
|
|
8
|
+
if (host === "0.0.0.0" || host === "")
|
|
9
|
+
target = "127.0.0.1";
|
|
10
|
+
else if (host === "::" || host === "[::]")
|
|
11
|
+
target = "::1";
|
|
12
|
+
const literal = target.includes(":") ? `[${target.replace(/^\[|\]$/g, "")}]` : target;
|
|
13
|
+
return `http://${literal}:${port}`;
|
|
14
|
+
}
|
|
15
|
+
var HEALTH_STATUSES = new Set(["healthy", "degraded", "unhealthy"]);
|
|
16
|
+
function isMeridianHealthBody(body) {
|
|
17
|
+
if (typeof body !== "object" || body === null)
|
|
18
|
+
return false;
|
|
19
|
+
const record = body;
|
|
20
|
+
if (typeof record["status"] === "string" && HEALTH_STATUSES.has(record["status"]) && typeof record["version"] === "string") {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
const error = record["error"];
|
|
24
|
+
if (record["type"] === "error" && typeof error === "object" && error !== null) {
|
|
25
|
+
return error["type"] === "authentication_error";
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
function describeForeignResponse(status, statusText, headers) {
|
|
30
|
+
const parts = [`HTTP ${status}${statusText ? ` ${statusText}` : ""}`];
|
|
31
|
+
const server = headers.get("server");
|
|
32
|
+
if (server)
|
|
33
|
+
parts.push(`server: ${server}`);
|
|
34
|
+
const contentType = headers.get("content-type");
|
|
35
|
+
if (contentType)
|
|
36
|
+
parts.push(contentType.split(";")[0].trim());
|
|
37
|
+
return parts.join(", ");
|
|
38
|
+
}
|
|
39
|
+
function isPortAvailable(host, port) {
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
const probe = createServer();
|
|
42
|
+
probe.once("error", () => resolve(false));
|
|
43
|
+
probe.once("listening", () => probe.close(() => resolve(true)));
|
|
44
|
+
probe.listen(port, host);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async function readJson(fetchImpl, url, timeoutMs, apiKey) {
|
|
48
|
+
try {
|
|
49
|
+
const response = await fetchImpl(url, {
|
|
50
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
51
|
+
headers: apiKey ? { "x-api-key": apiKey } : undefined
|
|
52
|
+
});
|
|
53
|
+
let body = null;
|
|
54
|
+
try {
|
|
55
|
+
body = await response.json();
|
|
56
|
+
} catch {
|
|
57
|
+
body = null;
|
|
58
|
+
}
|
|
59
|
+
return { ok: true, status: response.status, statusText: response.statusText, body, headers: response.headers };
|
|
60
|
+
} catch (err) {
|
|
61
|
+
return { ok: false, reason: err instanceof Error ? err.message : String(err) };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function readSection(fetchImpl, url, timeoutMs, apiKey) {
|
|
65
|
+
const result = await readJson(fetchImpl, url, timeoutMs, apiKey);
|
|
66
|
+
if (!result.ok)
|
|
67
|
+
return { data: null, reason: result.reason };
|
|
68
|
+
if (result.status === 401) {
|
|
69
|
+
return {
|
|
70
|
+
data: null,
|
|
71
|
+
reason: apiKey ? "the running instance rejected this MERIDIAN_API_KEY" : "the running instance requires an API key — set MERIDIAN_API_KEY"
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (result.status >= 400)
|
|
75
|
+
return { data: null, reason: `HTTP ${result.status}` };
|
|
76
|
+
if (result.body == null)
|
|
77
|
+
return { data: null, reason: "unreadable response" };
|
|
78
|
+
return { data: result.body };
|
|
79
|
+
}
|
|
80
|
+
async function probeMeridian(host, port, options = {}) {
|
|
81
|
+
const timeoutMs = options.timeoutMs ?? PROBE_TIMEOUT_MS;
|
|
82
|
+
const apiKey = options.apiKey || undefined;
|
|
83
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
84
|
+
const base = probeUrlBase(host, port);
|
|
85
|
+
const health = await readJson(fetchImpl, `${base}/health`, timeoutMs, apiKey);
|
|
86
|
+
if (!health.ok)
|
|
87
|
+
return { kind: "unreachable", description: health.reason };
|
|
88
|
+
if (!isMeridianHealthBody(health.body)) {
|
|
89
|
+
return {
|
|
90
|
+
kind: "foreign",
|
|
91
|
+
description: describeForeignResponse(health.status, health.statusText, health.headers)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const authRejected = health.status === 401;
|
|
95
|
+
const [profileList, quota, summary] = await Promise.all([
|
|
96
|
+
readSection(fetchImpl, `${base}/profiles/list`, timeoutMs, apiKey),
|
|
97
|
+
readSection(fetchImpl, `${base}/v1/usage/quota/all`, timeoutMs, apiKey),
|
|
98
|
+
readSection(fetchImpl, `${base}/telemetry/summary?window=86400000`, timeoutMs, apiKey)
|
|
99
|
+
]);
|
|
100
|
+
const unavailable = {};
|
|
101
|
+
const accountsReason = profileList.reason ?? quota.reason;
|
|
102
|
+
if (accountsReason)
|
|
103
|
+
unavailable.accounts = `accounts unavailable — ${accountsReason}`;
|
|
104
|
+
if (summary.reason)
|
|
105
|
+
unavailable.traffic = `traffic unavailable — ${summary.reason}`;
|
|
106
|
+
return {
|
|
107
|
+
kind: "meridian",
|
|
108
|
+
snapshot: {
|
|
109
|
+
health: authRejected ? null : health.body,
|
|
110
|
+
profileList: profileList.data,
|
|
111
|
+
quota: quota.data,
|
|
112
|
+
summary: summary.data,
|
|
113
|
+
...Object.keys(unavailable).length > 0 ? { unavailable } : {}
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function formatStatusMessage(result, host, port) {
|
|
118
|
+
if (result.kind === "foreign") {
|
|
119
|
+
return [
|
|
120
|
+
`Error: http://${host}:${port} is not Meridian (${result.description}).`,
|
|
121
|
+
` Something else owns that port. Point at the right one: MERIDIAN_PORT=<port> meridian status`
|
|
122
|
+
].join(`
|
|
123
|
+
`);
|
|
124
|
+
}
|
|
125
|
+
return [
|
|
126
|
+
`Error: no Meridian instance is listening on http://${host}:${port}.`,
|
|
127
|
+
` Start one with: meridian`
|
|
128
|
+
].join(`
|
|
129
|
+
`);
|
|
130
|
+
}
|
|
131
|
+
function formatConflictMessage(result, host, port) {
|
|
132
|
+
const found = result.kind === "foreign" ? `Something is listening there, and it is not Meridian (${result.description}).` : `Something is holding the port but did not answer (${result.description}).`;
|
|
133
|
+
return [
|
|
134
|
+
`Error: ${host}:${port} is already in use.`,
|
|
135
|
+
found,
|
|
136
|
+
"",
|
|
137
|
+
` Identify it with: lsof -i :${port}`,
|
|
138
|
+
` Or use a different port: MERIDIAN_PORT=4567 meridian`
|
|
139
|
+
].join(`
|
|
140
|
+
`);
|
|
141
|
+
}
|
|
142
|
+
export {
|
|
143
|
+
probeUrlBase,
|
|
144
|
+
probeMeridian,
|
|
145
|
+
isPortAvailable,
|
|
146
|
+
isMeridianHealthBody,
|
|
147
|
+
formatStatusMessage,
|
|
148
|
+
formatConflictMessage,
|
|
149
|
+
describeForeignResponse,
|
|
150
|
+
PROBE_TIMEOUT_MS
|
|
151
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/telemetry/dashboard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,eAAO,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/telemetry/dashboard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,eAAO,MAAM,aAAa,QA8mBlB,CAAA"}
|
|
@@ -1,12 +1,30 @@
|
|
|
1
|
-
import type { ITelemetryStore, IDiagnosticLogStore } from "./types";
|
|
1
|
+
import type { ITelemetryStore, IDiagnosticLogStore, ResolvedTelemetryConfig } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* What this process WOULD use if it started right now.
|
|
4
|
+
*
|
|
5
|
+
* Called at startup to build the stores, and again by the settings API to
|
|
6
|
+
* detect a pending restart — the same function both times on purpose. A
|
|
7
|
+
* second copy of this precedence chain living in the settings route is how a
|
|
8
|
+
* page ends up confidently reporting a state the proxy is not in.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveTelemetryConfig(): ResolvedTelemetryConfig;
|
|
2
11
|
export declare const telemetryStore: ITelemetryStore;
|
|
3
12
|
export declare const diagnosticLog: IDiagnosticLogStore;
|
|
13
|
+
/**
|
|
14
|
+
* Entries the running diagnostic ring holds, null when persisting.
|
|
15
|
+
*
|
|
16
|
+
* The metric store reports its own shape through `describe()`; the log store
|
|
17
|
+
* has no such method, and adding one to IDiagnosticLogStore for a single
|
|
18
|
+
* number would widen an interface two classes implement.
|
|
19
|
+
*/
|
|
20
|
+
export declare const diagnosticLogCapacity: number | null;
|
|
4
21
|
export { MemoryTelemetryStore } from "./store";
|
|
5
22
|
export { MemoryDiagnosticLogStore } from "./logStore";
|
|
6
23
|
export { createTelemetryRoutes } from "./routes";
|
|
7
24
|
export { landingHtml } from "./landing";
|
|
8
25
|
export { computePercentiles, computeSummary } from "./percentiles";
|
|
26
|
+
export { collapseRouteChains, summarizeRoutes } from "./routeChain";
|
|
9
27
|
export { renderPrometheusMetrics } from "./prometheus";
|
|
10
28
|
export { createSqliteStores } from "./sqlite";
|
|
11
|
-
export type { RequestMetric, TelemetrySummary, PhaseTiming, ITelemetryStore, IDiagnosticLogStore, DiagnosticLog, } from "./types";
|
|
29
|
+
export type { RequestMetric, RouteHop, RouteKind, RouteProfileTally, RouteSummary, ResolvedTelemetryConfig, TelemetryRetention, TelemetrySummary, PhaseTiming, ITelemetryStore, IDiagnosticLogStore, DiagnosticLog, } from "./types";
|
|
12
30
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/telemetry/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA;AA6B5F;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,IAAI,uBAAuB,CAWhE;AA6CD,eAAO,MAAM,cAAc,EAAE,eAAkC,CAAA;AAC/D,eAAO,MAAM,aAAa,EAAE,mBAAwC,CAAA;AAEpE;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,GAAG,IAAmC,CAAA;AAEhF,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAClE,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAC7C,YAAY,EACV,aAAa,EACb,QAAQ,EACR,SAAS,EACT,iBAAiB,EACjB,YAAY,EACZ,uBAAuB,EACvB,kBAAkB,EAClB,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,aAAa,GACd,MAAM,SAAS,CAAA"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* (usage + est. cost, click to switch the active profile), and a compact
|
|
6
6
|
* 24h traffic strip. Site chrome (logo, nav, status) lives in the shared
|
|
7
7
|
* header from profileBar.ts. Fetches /health, /telemetry/summary,
|
|
8
|
-
* /v1/usage/quota/all
|
|
8
|
+
* /v1/usage/quota/all, /profiles/list and /settings/api/routing client-side for live data.
|
|
9
9
|
*/
|
|
10
10
|
export declare const landingHtml: string;
|
|
11
11
|
//# sourceMappingURL=landing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"landing.d.ts","sourceRoot":"","sources":["../../src/telemetry/landing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"landing.d.ts","sourceRoot":"","sources":["../../src/telemetry/landing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,eAAO,MAAM,WAAW,QAigBhB,CAAA"}
|
|
@@ -7,6 +7,17 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { DiagnosticLog, IDiagnosticLogStore } from "./types";
|
|
9
9
|
export type { DiagnosticLog } from "./types";
|
|
10
|
+
/**
|
|
11
|
+
* Tunable for the same reason the metric ring is: 500 entries is the tightest
|
|
12
|
+
* window in the telemetry system - the metric ring holds twice as many rows of
|
|
13
|
+
* a rarer event - and it was the only one with no way to change it, so the
|
|
14
|
+
* logs a bug report needs had usually scrolled away by the time anyone looked.
|
|
15
|
+
*
|
|
16
|
+
* Env beats the saved setting, which beats the default. A value that is not
|
|
17
|
+
* positive is discarded rather than falling through, so a typo cannot produce
|
|
18
|
+
* a zero-length ring.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveDiagnosticLogCapacity(): number;
|
|
10
21
|
export declare class MemoryDiagnosticLogStore implements IDiagnosticLogStore {
|
|
11
22
|
private buffer;
|
|
12
23
|
private head;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logStore.d.ts","sourceRoot":"","sources":["../../src/telemetry/logStore.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"logStore.d.ts","sourceRoot":"","sources":["../../src/telemetry/logStore.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AACjE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAI5C;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,CAQrD;AAED,qBAAa,wBAAyB,YAAW,mBAAmB;IAClE,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,IAAI,CAAI;IAChB,OAAO,CAAC,KAAK,CAAI;IACjB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;gBAErB,QAAQ,CAAC,EAAE,MAAM;IAK7B,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,GAAG,IAAI;IAMlD,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAIlD,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAIlD,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IAIhD,SAAS,CAAC,OAAO,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,aAAa,EAAE;IAgB/F,KAAK,IAAI,IAAI;CAKd;AAED,4CAA4C;AAC5C,eAAO,MAAM,aAAa,0BAAiC,CAAA"}
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* static table in pricing.ts doesn't know about (custom routers, brand-new
|
|
6
6
|
* releases). Overrides win over the built-in table in resolveModelPricing.
|
|
7
7
|
*
|
|
8
|
-
* Persisted to
|
|
9
|
-
* MERIDIAN_PRICING_CONFIG). Read at request time with a short cache, so
|
|
8
|
+
* Persisted to model-pricing.json in the config directory (override the path
|
|
9
|
+
* with MERIDIAN_PRICING_CONFIG). Read at request time with a short cache, so
|
|
10
10
|
* settings-page edits show up on the next dashboard refresh without a proxy
|
|
11
11
|
* restart. Mirrors the sdkFeatures.ts persistence pattern.
|
|
12
12
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pricingStore.d.ts","sourceRoot":"","sources":["../../src/telemetry/pricingStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,WAAW,CAAA;AAElB,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"pricingStore.d.ts","sourceRoot":"","sources":["../../src/telemetry/pricingStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,EAIL,KAAK,YAAY,EAClB,MAAM,WAAW,CAAA;AAElB,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;AA6D3D,gFAAgF;AAChF,wBAAgB,mBAAmB,IAAI,gBAAgB,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,CAwBhE;AAED,uEAAuE;AACvE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOtD;AAED,0DAA0D;AAC1D,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI,CAI7E;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAKzD;AAED,4EAA4E;AAC5E,wBAAgB,0BAA0B,IAAI,IAAI,CAIjD"}
|
|
@@ -27,9 +27,9 @@ export declare const themeCss = "\n :root {\n /* Cool-gray neutral palette.
|
|
|
27
27
|
* pole. Scales cleanly from favicon to hero size.
|
|
28
28
|
*/
|
|
29
29
|
export declare const meridianLogoSvg = "<svg class=\"mh-logo\" width=\"24\" height=\"24\" viewBox=\"0 0 64 64\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <defs>\n <linearGradient id=\"mhGrad\" x1=\"32\" y1=\"4\" x2=\"32\" y2=\"60\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#58a6ff\"/>\n <stop offset=\"1\" stop-color=\"#bc8cff\"/>\n </linearGradient>\n </defs>\n <circle cx=\"32\" cy=\"32\" r=\"25\" stroke=\"url(#mhGrad)\" stroke-width=\"3.5\"/>\n <ellipse cx=\"32\" cy=\"32\" rx=\"10.5\" ry=\"25\" stroke=\"url(#mhGrad)\" stroke-width=\"2.5\" opacity=\"0.8\"/>\n <path d=\"M7 32h50\" stroke=\"url(#mhGrad)\" stroke-width=\"2\" opacity=\"0.4\"/>\n <circle cx=\"32\" cy=\"7\" r=\"4.5\" fill=\"#58a6ff\"/>\n <circle cx=\"32\" cy=\"57\" r=\"4.5\" fill=\"#bc8cff\"/>\n</svg>";
|
|
30
|
-
export declare const profileBarCss = "\n .meridian-header {\n position: sticky; top: 0; z-index: 100;\n display: flex; align-items: center; gap: 20px;\n padding: 10px 24px;\n background: rgba(13, 17, 23, 0.92);\n backdrop-filter: blur(12px);\n border-bottom: 1px solid var(--border, #30363d);\n }\n .meridian-header .mh-brand {\n display: flex; align-items: center; gap: 10px;\n text-decoration: none; color: var(--text, #e6edf3);\n }\n .meridian-header .mh-logo { display: block; }\n .meridian-header .mh-name {\n font-size: 15px; font-weight: 700; letter-spacing: 2px;\n text-transform: uppercase;\n }\n .meridian-header .mh-nav { display: flex; align-items: center; gap: 2px; }\n .meridian-header .mh-nav a {\n color: var(--muted, #8b949e); text-decoration: none; font-size: 12px;\n font-weight: 500; padding: 5px 10px; border-radius: 6px;\n transition: color 0.15s, background 0.15s;\n }\n .meridian-header .mh-nav a:hover { color: var(--text, #e6edf3); background: var(--surface, #161b22); }\n .meridian-header .mh-nav a.active { color: var(--accent, #58a6ff); background: var(--surface, #161b22); }\n .meridian-header .mh-right {\n margin-left: auto; display: flex; align-items: center; gap: 10px;\n }\n .meridian-header .mh-profile {\n display: none; align-items: center; gap: 6px;\n font-size: 11px; font-weight: 500; color: var(--text, #e6edf3);\n padding: 3px 10px; border-radius: 20px;\n background: var(--surface, #161b22); border: 1px solid var(--border, #30363d);\n text-decoration: none; transition: border-color 0.15s;\n }\n .meridian-header .mh-profile:hover { border-color: var(--accent, #58a6ff); }\n .meridian-header .mh-profile.visible { display: inline-flex; }\n .meridian-header .mh-profile .mh-profile-type {\n color: var(--muted, #8b949e); font-size: 10px;\n }\n /* Build chip \u2014 two mutually exclusive states, and the colour is not a\n style choice: \"update available\" is a link to the releases page, so it\n is blue (interactive); \"local/dev build\" is a meta annotation with no\n href, so it is violet. Swapping them would break the DESIGN.md rule. */\n .meridian-header .mh-build {\n display: none; align-items: center; gap: 6px;\n font-size: 11px; font-weight: 500; white-space: nowrap;\n padding: 3px 10px; border-radius: 20px; text-decoration: none;\n transition: background 0.15s;\n }\n .meridian-header .mh-build.visible { display: inline-flex; }\n .meridian-header .mh-build.update {\n color: var(--accent, #58a6ff);\n background: rgba(88,166,255,0.12);\n border: 1px solid rgba(88,166,255,0.35);\n }\n .meridian-header .mh-build.update:hover { background: rgba(88,166,255,0.18); }\n .meridian-header .mh-build.provenance {\n color: var(--accent2, #bc8cff);\n background: rgba(188,140,255,0.12);\n border: 1px solid rgba(188,140,255,0.35);\n cursor: default;\n }\n .meridian-header .mh-status {\n display: inline-flex; align-items: center; gap: 6px;\n font-size: 11px; color: var(--muted, #8b949e); white-space: nowrap;\n }\n .meridian-header .mh-dot {\n width: 8px; height: 8px; border-radius: 50%;\n background: var(--muted, #8b949e); flex-shrink: 0;\n }\n .meridian-header .mh-dot.healthy { background: var(--green, #3fb950); box-shadow: 0 0 6px rgba(63,185,80,0.5); }\n .meridian-header .mh-dot.degraded { background: var(--yellow, #d29922); }\n .meridian-header .mh-dot.unhealthy { background: var(--red, #f85149); }\n @media (max-width: 720px) {\n .meridian-header { gap: 10px; padding: 10px 16px; flex-wrap: wrap; }\n .meridian-header .mh-name { display: none; }\n .meridian-header .mh-status .mh-status-text { display: none; }\n }\n";
|
|
30
|
+
export declare const profileBarCss = "\n .meridian-header {\n position: sticky; top: 0; z-index: 100;\n display: flex; align-items: center; gap: 20px;\n padding: 10px 24px;\n background: rgba(13, 17, 23, 0.92);\n backdrop-filter: blur(12px);\n border-bottom: 1px solid var(--border, #30363d);\n }\n .meridian-header .mh-brand {\n display: flex; align-items: center; gap: 10px;\n text-decoration: none; color: var(--text, #e6edf3);\n }\n .meridian-header .mh-logo { display: block; }\n .meridian-header .mh-name {\n font-size: 15px; font-weight: 700; letter-spacing: 2px;\n text-transform: uppercase;\n }\n .meridian-header .mh-nav { display: flex; align-items: center; gap: 2px; }\n .meridian-header .mh-nav a {\n color: var(--muted, #8b949e); text-decoration: none; font-size: 12px;\n font-weight: 500; padding: 5px 10px; border-radius: 6px;\n transition: color 0.15s, background 0.15s;\n }\n .meridian-header .mh-nav a:hover { color: var(--text, #e6edf3); background: var(--surface, #161b22); }\n .meridian-header .mh-nav a.active { color: var(--accent, #58a6ff); background: var(--surface, #161b22); }\n .meridian-header .mh-right {\n margin-left: auto; display: flex; align-items: center; gap: 10px;\n }\n .meridian-header .mh-profile {\n display: none; align-items: center; gap: 6px;\n font-size: 11px; font-weight: 500; color: var(--text, #e6edf3);\n padding: 3px 10px; border-radius: 20px;\n background: var(--surface, #161b22); border: 1px solid var(--border, #30363d);\n text-decoration: none; transition: border-color 0.15s;\n }\n .meridian-header .mh-profile:hover { border-color: var(--accent, #58a6ff); }\n .meridian-header .mh-profile.visible { display: inline-flex; }\n .meridian-header .mh-profile .mh-profile-type {\n color: var(--muted, #8b949e); font-size: 10px;\n }\n /* Build chip \u2014 two mutually exclusive states, and the colour is not a\n style choice: \"update available\" is a link to the releases page, so it\n is blue (interactive); \"local/dev build\" is a meta annotation with no\n href, so it is violet. Swapping them would break the DESIGN.md rule. */\n .meridian-header .mh-build {\n display: none; align-items: center; gap: 6px;\n font-size: 11px; font-weight: 500; white-space: nowrap;\n padding: 3px 10px; border-radius: 20px; text-decoration: none;\n transition: background 0.15s;\n }\n .meridian-header .mh-build.visible { display: inline-flex; }\n .meridian-header .mh-build.update {\n color: var(--accent, #58a6ff);\n background: rgba(88,166,255,0.12);\n border: 1px solid rgba(88,166,255,0.35);\n }\n .meridian-header .mh-build.update:hover { background: rgba(88,166,255,0.18); }\n .meridian-header .mh-build.provenance {\n color: var(--accent2, #bc8cff);\n background: rgba(188,140,255,0.12);\n border: 1px solid rgba(188,140,255,0.35);\n cursor: default;\n .meridian-header .mh-profile.following { border-color: var(--accent2, #bc8cff); }\n .meridian-header .mh-profile .mh-profile-follow {\n color: var(--accent2, #bc8cff); font-size: 10px; }\n .meridian-header .mh-status {\n display: inline-flex; align-items: center; gap: 6px;\n font-size: 11px; color: var(--muted, #8b949e); white-space: nowrap;\n }\n .meridian-header .mh-dot {\n width: 8px; height: 8px; border-radius: 50%;\n background: var(--muted, #8b949e); flex-shrink: 0;\n }\n .meridian-header .mh-dot.healthy { background: var(--green, #3fb950); box-shadow: 0 0 6px rgba(63,185,80,0.5); }\n .meridian-header .mh-dot.degraded { background: var(--yellow, #d29922); }\n .meridian-header .mh-dot.unhealthy { background: var(--red, #f85149); }\n @media (max-width: 720px) {\n .meridian-header { gap: 10px; padding: 10px 16px; flex-wrap: wrap; }\n .meridian-header .mh-name { display: none; }\n .meridian-header .mh-status .mh-status-text { display: none; }\n }\n";
|
|
31
31
|
export declare const profileBarHtml = "\n<header class=\"meridian-header\" id=\"meridianHeader\">\n <a class=\"mh-brand\" href=\"/\">\n <svg class=\"mh-logo\" width=\"24\" height=\"24\" viewBox=\"0 0 64 64\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <defs>\n <linearGradient id=\"mhGrad\" x1=\"32\" y1=\"4\" x2=\"32\" y2=\"60\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#58a6ff\"/>\n <stop offset=\"1\" stop-color=\"#bc8cff\"/>\n </linearGradient>\n </defs>\n <circle cx=\"32\" cy=\"32\" r=\"25\" stroke=\"url(#mhGrad)\" stroke-width=\"3.5\"/>\n <ellipse cx=\"32\" cy=\"32\" rx=\"10.5\" ry=\"25\" stroke=\"url(#mhGrad)\" stroke-width=\"2.5\" opacity=\"0.8\"/>\n <path d=\"M7 32h50\" stroke=\"url(#mhGrad)\" stroke-width=\"2\" opacity=\"0.4\"/>\n <circle cx=\"32\" cy=\"7\" r=\"4.5\" fill=\"#58a6ff\"/>\n <circle cx=\"32\" cy=\"57\" r=\"4.5\" fill=\"#bc8cff\"/>\n</svg>\n <span class=\"mh-name\">Meridian</span>\n </a>\n <nav class=\"mh-nav\">\n <a href=\"/\" id=\"nav-home\">Home</a>\n <a href=\"/telemetry\" id=\"nav-telemetry\">Telemetry</a>\n <a href=\"/profiles\" id=\"nav-profiles\">Profiles</a>\n <a href=\"/settings\" id=\"nav-settings\">Settings</a>\n <a href=\"/plugins\" id=\"nav-plugins\">Plugins</a>\n </nav>\n <div class=\"mh-right\">\n <a class=\"mh-build\" id=\"mhBuild\" target=\"_blank\" rel=\"noopener\"></a>\n <a class=\"mh-profile\" id=\"mhProfile\" href=\"/\" title=\"Active profile \u2014 switch from the home page\"></a>\n <span class=\"mh-status\" id=\"mhStatus\"><span class=\"mh-dot\" id=\"mhDot\"></span><span class=\"mh-status-text\" id=\"mhStatusText\"></span></span>\n </div>\n</header>\n";
|
|
32
|
-
export declare const profileBarJs = "\n(function() {\n var profileChip = document.getElementById('mhProfile');\n var buildChip = document.getElementById('mhBuild');\n var statusDot = document.getElementById('mhDot');\n var statusText = document.getElementById('mhStatusText');\n\n // Highlight active nav link\n var path = location.pathname;\n var navLinks = document.querySelectorAll('.mh-nav a');\n navLinks.forEach(function(a) {\n if (a.getAttribute('href') === path || (path === '/telemetry' && a.id === 'nav-telemetry') || (path === '/' && a.id === 'nav-home')) {\n a.classList.add('active');\n }\n });\n\n function esc(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; }\n\n // Build provenance chip. Hidden entirely for a current npm install, which\n // is the case that needs no comment.\n function renderBuild(build) {\n if (!buildChip) return;\n if (!build) { buildChip.className = 'mh-build'; return; }\n if (build.source !== 'npm') {\n buildChip.textContent = (build.source === 'dev' ? 'dev build' : 'local build') + (build.dirty ? ' *' : '');\n buildChip.removeAttribute('href');\n buildChip.title = 'Not an npm release \u2014 the reported version ' + build.version +\n ' is the tree\\'s last release, not proof of what is running' +\n (build.branch ? '\\nbranch: ' + build.branch : '') +\n (build.sha ? '\\ncommit: ' + build.sha.slice(0, 8) : '') +\n (build.dirty ? '\\nuncommitted changes present' : '');\n buildChip.className = 'mh-build provenance visible';\n return;\n }\n if (build.updateAvailable) {\n buildChip.textContent = build.latest + ' available';\n buildChip.href = 'https://github.com/rynfar/meridian/releases';\n buildChip.title = 'Running ' + build.version + ' \u2014 update with:\\nnpm install -g @rynfar/meridian@latest';\n buildChip.className = 'mh-build update visible';\n return;\n }\n buildChip.className = 'mh-build';\n }\n\n function loadHeader() {\n fetch('/health').then(function(r) { return r.json(); }).then(function(h) {\n var st = h.status === 'healthy' ? 'healthy' : h.status === 'degraded' ? 'degraded' : 'unhealthy';\n statusDot.className = 'mh-dot ' + st;\n statusText.textContent = st === 'healthy' ? 'Operational' : st === 'degraded' ? 'Degraded' : 'Offline';\n renderBuild(h.build);\n }).catch(function() {\n statusDot.className = 'mh-dot unhealthy';\n statusText.textContent = 'Offline';\n });\n\n fetch('/profiles/list').then(function(r) { return r.json(); }).then(function(data) {\n var current = (data.profiles || []).find(function(p) { return p.isActive; });\n if (!current) { profileChip.classList.remove('visible'); return; }\n profileChip.innerHTML = esc(current.id) + ' <span class=\"mh-profile-type\">' + esc(current.type || '') + '</span>';\n profileChip.classList.add('visible');\n }).catch(function() {});\n }\n\n loadHeader();\n setInterval(loadHeader, 10000);\n // Pages call this after mutating state (e.g. switching the active profile)\n // so the header chip updates immediately instead of on the next poll.\n window.meridianHeaderRefresh = loadHeader;\n})();\n";
|
|
32
|
+
export declare const profileBarJs = "\n(function() {\n var profileChip = document.getElementById('mhProfile');\n var buildChip = document.getElementById('mhBuild');\n var statusDot = document.getElementById('mhDot');\n var statusText = document.getElementById('mhStatusText');\n\n // Highlight active nav link\n var path = location.pathname;\n var navLinks = document.querySelectorAll('.mh-nav a');\n navLinks.forEach(function(a) {\n if (a.getAttribute('href') === path || (path === '/telemetry' && a.id === 'nav-telemetry') || (path === '/' && a.id === 'nav-home')) {\n a.classList.add('active');\n }\n });\n\n function esc(s) { var d = document.createElement('div'); d.textContent = s; return d.innerHTML; }\n\n // Build provenance chip. Hidden entirely for a current npm install, which\n // is the case that needs no comment.\n function renderBuild(build) {\n if (!buildChip) return;\n if (!build) { buildChip.className = 'mh-build'; return; }\n if (build.source !== 'npm') {\n buildChip.textContent = (build.source === 'dev' ? 'dev build' : 'local build') + (build.dirty ? ' *' : '');\n buildChip.removeAttribute('href');\n buildChip.title = 'Not an npm release \u2014 the reported version ' + build.version +\n ' is the tree\\'s last release, not proof of what is running' +\n (build.branch ? '\\nbranch: ' + build.branch : '') +\n (build.sha ? '\\ncommit: ' + build.sha.slice(0, 8) : '') +\n (build.dirty ? '\\nuncommitted changes present' : '');\n buildChip.className = 'mh-build provenance visible';\n return;\n }\n if (build.updateAvailable) {\n buildChip.textContent = build.latest + ' available';\n buildChip.href = 'https://github.com/rynfar/meridian/releases';\n buildChip.title = 'Running ' + build.version + ' \u2014 update with:\\nnpm install -g @rynfar/meridian@latest';\n buildChip.className = 'mh-build update visible';\n return;\n }\n buildChip.className = 'mh-build';\n }\n\n function loadHeader() {\n fetch('/health').then(function(r) { return r.json(); }).then(function(h) {\n var st = h.status === 'healthy' ? 'healthy' : h.status === 'degraded' ? 'degraded' : 'unhealthy';\n statusDot.className = 'mh-dot ' + st;\n statusText.textContent = st === 'healthy' ? 'Operational' : st === 'degraded' ? 'Degraded' : 'Offline';\n renderBuild(h.build);\n }).catch(function() {\n statusDot.className = 'mh-dot unhealthy';\n statusText.textContent = 'Offline';\n });\n\n fetch('/profiles/list').then(function(r) { return r.json(); }).then(function(data) {\n var current = (data.profiles || []).find(function(p) { return p.isActive; });\n if (!current) { profileChip.classList.remove('visible'); return; }\n // Follow mode: say so on every page. An instance quietly taking its\n // active profile from another one, with a picker that won't stick, is\n // otherwise an hour of confusion.\n var follow = data.follow;\n var followLabel = follow ? (follow.activeProfile ? 'following' : 'follow: local') : '';\n if (follow && follow.stale) followLabel += ' (stale)';\n profileChip.innerHTML = esc(current.id) + ' <span class=\"mh-profile-type\">' + esc(current.type || '') + '</span>'\n + (follow ? ' <span class=\"mh-profile-follow\">' + esc(followLabel) + '</span>' : '');\n profileChip.classList.toggle('following', !!follow);\n profileChip.title = follow\n ? 'Active profile follows ' + follow.url + ' (MERIDIAN_FOLLOW_ACTIVE)'\n + (follow.activeProfile ? '' : ' \u2014 no usable value from it, using the local profile')\n + '. Switching here is refused; switch on the followed instance.'\n : 'Active profile \u2014 switch from the home page';\n profileChip.classList.add('visible');\n }).catch(function() {});\n }\n\n loadHeader();\n setInterval(loadHeader, 10000);\n // Pages call this after mutating state (e.g. switching the active profile)\n // so the header chip updates immediately instead of on the next poll.\n window.meridianHeaderRefresh = loadHeader;\n})();\n";
|
|
33
33
|
/** Native desktop chrome: system appearance with the canonical Meridian hues.
|
|
34
34
|
* Web pages continue using themeCss and the shared profile header above. */
|
|
35
35
|
export declare const desktopThemeCss = "\n :root {\n color-scheme: light dark;\n --bg: #f4f5f7; --surface: #ffffff; --surface2: #edeff2;\n --border: #dde0e5; --text: #23272e; --muted: #69717e;\n --accent: #176bce; --accent2: #8250b5;\n --green: #237c40; --yellow: #976400; --red: #ca3b36;\n --sidebar: #e9edf1; --control: #ffffff; --control-border: #d0d5dc;\n }\n @media (prefers-color-scheme: dark) {\n :root {\n --bg: #0d1117; --surface: #161b22; --surface2: #1c2128;\n --border: #30363d; --text: #e6edf3; --muted: #8b949e;\n --accent: #58a6ff; --accent2: #bc8cff;\n --green: #3fb950; --yellow: #d29922; --red: #f85149;\n --sidebar: #161b22; --control: #242b35; --control-border: #39424e;\n }\n }\n body { background: var(--bg); }\n html.native-glass body { background: transparent; }\n";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"profileBar.d.ts","sourceRoot":"","sources":["../../src/telemetry/profileBar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,6gDAwCpB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,syBAYrB,CAAA;AAEP,eAAO,MAAM,aAAa,
|
|
1
|
+
{"version":3,"file":"profileBar.d.ts","sourceRoot":"","sources":["../../src/telemetry/profileBar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,6gDAwCpB,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,syBAYrB,CAAA;AAEP,eAAO,MAAM,aAAa,i0HAkFzB,CAAA;AAED,eAAO,MAAM,cAAc,ypDAmB1B,CAAA;AAED,eAAO,MAAM,YAAY,6hIAkFxB,CAAA;AAED;4EAC4E;AAC5E,eAAO,MAAM,eAAe,wyBAoB3B,CAAA;AAED,eAAO,MAAM,mBAAmB;;;;CAA2E,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a profile card states about an account, in one place.
|
|
3
|
+
*
|
|
4
|
+
* Two surfaces render the same list — the detail grid on /profiles and the
|
|
5
|
+
* hover overlay on the landing page — so the list lives here instead of being
|
|
6
|
+
* written twice and drifting apart the first time a row is added.
|
|
7
|
+
*
|
|
8
|
+
* Emitted as browser source rather than a TypeScript function because the
|
|
9
|
+
* pages are string templates concatenated at import time, the same arrangement
|
|
10
|
+
* `profileBarJs` uses. The unit tests evaluate this exact text, so what they
|
|
11
|
+
* assert is what the browser runs.
|
|
12
|
+
*
|
|
13
|
+
* Values are plain text; escaping is the page's job, since only the page knows
|
|
14
|
+
* whether it is filling a grid cell or an overlay row.
|
|
15
|
+
*/
|
|
16
|
+
export declare const profileFactsJs = "\nfunction timeAgo(ts) {\n if (!ts) return '\\u2014';\n var s = Math.floor((Date.now() - ts) / 1000);\n if (s < 5) return 'just now';\n if (s < 60) return s + 's ago';\n if (s < 3600) return Math.floor(s / 60) + 'm ago';\n if (s < 86400) return Math.floor(s / 3600) + 'h ago';\n return new Date(ts).toLocaleString();\n}\n\nfunction profileFacts(p) {\n var facts = [];\n var authStale = p.authProvenance && p.authProvenance !== 'live';\n var statusValue = p.authProvenance === 'never' ? 'never read' : (p.loggedIn ? '\\u2713 Authenticated' : '\\u2717 Not logged in');\n facts.push({\n label: 'Status',\n value: statusValue,\n tone: p.loggedIn ? 'ok' : 'err',\n cached: authStale && p.authProvenance !== 'never'\n });\n if (p.email) facts.push({ label: 'Email', value: p.email, tone: '', cached: authStale });\n if (p.organizationName) facts.push({ label: 'Organization', value: p.organizationName, tone: '' });\n if (p.accountType) facts.push({ label: 'Account', value: p.accountType, tone: '' });\n var plan = p.planName || p.planLabel || p.subscriptionType;\n if (plan) facts.push({ label: 'Plan', value: plan, tone: '', cached: authStale, title: p.seatTier || p.rateLimitTier || '' });\n if (p.allowance) facts.push({ label: 'Allowance', value: p.allowance + ' of a Pro plan\\u2019s Claude Code usage', shortValue: p.allowance, tone: '', title: p.rateLimitTier || '' });\n if (p.aliases && p.aliases.length > 0) facts.push({ label: 'Former names', value: p.aliases.join(', '), tone: '', title: 'Requests naming these are served by this profile, until the name is added again' });\n if (p.lastSuccessAt) facts.push({ label: 'Last Verified', value: timeAgo(p.lastSuccessAt), tone: 'ok' });\n if (p.lastCheckedAt && p.lastCheckedAt !== p.lastSuccessAt) {\n facts.push({ label: 'Last Checked', value: timeAgo(p.lastCheckedAt), tone: '' });\n }\n return facts;\n}\n";
|
|
17
|
+
//# sourceMappingURL=profileFacts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profileFacts.d.ts","sourceRoot":"","sources":["../../src/telemetry/profileFacts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,cAAc,i3DAkC1B,CAAA"}
|