@eddyskywalker/dsh-chatgpt-subscription 0.46.1 → 0.47.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 (90) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +22 -1
  3. package/lib/client.js +2639 -571
  4. package/lib/client.js.map +1 -1
  5. package/lib/index.js +12000 -3591
  6. package/lib/plan-mode-pipeline.js +419 -0
  7. package/lib/types/client/hub/brand-icons.d.ts +1 -1
  8. package/lib/types/client/hub/brand-icons.d.ts.map +1 -1
  9. package/lib/types/client/hub/brand-svg.d.ts +4 -0
  10. package/lib/types/client/hub/brand-svg.d.ts.map +1 -1
  11. package/lib/types/client/hub/providers.d.ts.map +1 -1
  12. package/lib/types/client/index.d.ts +2 -0
  13. package/lib/types/client/index.d.ts.map +1 -1
  14. package/lib/types/client/locales-ru/index.d.ts.map +1 -1
  15. package/lib/types/client/locales-ru/progress-updates.d.ts.map +1 -1
  16. package/lib/types/client/locales-ru/workbuddy.d.ts +10 -0
  17. package/lib/types/client/locales-ru/workbuddy.d.ts.map +1 -0
  18. package/lib/types/client/progress-updates/locales.d.ts +23 -5
  19. package/lib/types/client/progress-updates/locales.d.ts.map +1 -1
  20. package/lib/types/client/progress-updates/settings-binding.d.ts +13 -1
  21. package/lib/types/client/progress-updates/settings-binding.d.ts.map +1 -1
  22. package/lib/types/client/progress-updates/settings-section.d.ts +1 -1
  23. package/lib/types/client/progress-updates/settings-section.d.ts.map +1 -1
  24. package/lib/types/client/workbuddy/WorkBuddyComposerQuota.d.ts +34 -0
  25. package/lib/types/client/workbuddy/WorkBuddyComposerQuota.d.ts.map +1 -0
  26. package/lib/types/client/workbuddy/WorkBuddySection.d.ts +91 -0
  27. package/lib/types/client/workbuddy/WorkBuddySection.d.ts.map +1 -0
  28. package/lib/types/client/workbuddy/locales.d.ts +768 -0
  29. package/lib/types/client/workbuddy/locales.d.ts.map +1 -0
  30. package/lib/types/client/workbuddy/styles.d.ts +21 -0
  31. package/lib/types/client/workbuddy/styles.d.ts.map +1 -0
  32. package/lib/types/client/workbuddy/useWorkBuddySection.d.ts +149 -0
  33. package/lib/types/client/workbuddy/useWorkBuddySection.d.ts.map +1 -0
  34. package/lib/types/compat.d.ts +1 -1
  35. package/lib/types/entry-plan-mode-pipeline.d.ts +191 -0
  36. package/lib/types/entry-plan-mode-pipeline.d.ts.map +1 -0
  37. package/lib/types/host/agent-preset.d.ts.map +1 -1
  38. package/lib/types/host/cache-warmup/contracts.d.ts +5 -1
  39. package/lib/types/host/cache-warmup/contracts.d.ts.map +1 -1
  40. package/lib/types/host/cache-warmup/install.d.ts.map +1 -1
  41. package/lib/types/host/cache-warmup/projection.d.ts.map +1 -1
  42. package/lib/types/host/cache-warmup/retrieval-intent.d.ts +18 -0
  43. package/lib/types/host/cache-warmup/retrieval-intent.d.ts.map +1 -0
  44. package/lib/types/host/cache-warmup/settings.d.ts +9 -2
  45. package/lib/types/host/cache-warmup/settings.d.ts.map +1 -1
  46. package/lib/types/host/common/capabilities.d.ts.map +1 -1
  47. package/lib/types/host/common/model-request-control.d.ts +1 -1
  48. package/lib/types/host/common/model-request-control.d.ts.map +1 -1
  49. package/lib/types/host/common/request-diagnostics.d.ts +1 -1
  50. package/lib/types/host/common/request-diagnostics.d.ts.map +1 -1
  51. package/lib/types/host/compaction-prompt.d.ts +129 -0
  52. package/lib/types/host/compaction-prompt.d.ts.map +1 -0
  53. package/lib/types/host/preset-sync.d.ts.map +1 -1
  54. package/lib/types/host/workbuddy/account-pool.d.ts +389 -0
  55. package/lib/types/host/workbuddy/account-pool.d.ts.map +1 -0
  56. package/lib/types/host/workbuddy/adapter.d.ts +249 -0
  57. package/lib/types/host/workbuddy/adapter.d.ts.map +1 -0
  58. package/lib/types/host/workbuddy/atrest.d.ts +98 -0
  59. package/lib/types/host/workbuddy/atrest.d.ts.map +1 -0
  60. package/lib/types/host/workbuddy/checkin.d.ts +150 -0
  61. package/lib/types/host/workbuddy/checkin.d.ts.map +1 -0
  62. package/lib/types/host/workbuddy/client.d.ts +214 -0
  63. package/lib/types/host/workbuddy/client.d.ts.map +1 -0
  64. package/lib/types/host/workbuddy/dsml.d.ts +55 -0
  65. package/lib/types/host/workbuddy/dsml.d.ts.map +1 -0
  66. package/lib/types/host/workbuddy/guardrails.d.ts +111 -0
  67. package/lib/types/host/workbuddy/guardrails.d.ts.map +1 -0
  68. package/lib/types/host/workbuddy/identity.d.ts +71 -0
  69. package/lib/types/host/workbuddy/identity.d.ts.map +1 -0
  70. package/lib/types/host/workbuddy/mapper.d.ts +238 -0
  71. package/lib/types/host/workbuddy/mapper.d.ts.map +1 -0
  72. package/lib/types/host/workbuddy/model-catalog.d.ts +217 -0
  73. package/lib/types/host/workbuddy/model-catalog.d.ts.map +1 -0
  74. package/lib/types/host/workbuddy/oauth.d.ts +47 -0
  75. package/lib/types/host/workbuddy/oauth.d.ts.map +1 -0
  76. package/lib/types/host/workbuddy/outbound-identity.d.ts +139 -0
  77. package/lib/types/host/workbuddy/outbound-identity.d.ts.map +1 -0
  78. package/lib/types/host/workbuddy/routes.d.ts +56 -0
  79. package/lib/types/host/workbuddy/routes.d.ts.map +1 -0
  80. package/lib/types/host/workbuddy/token-store.d.ts +370 -0
  81. package/lib/types/host/workbuddy/token-store.d.ts.map +1 -0
  82. package/lib/types/host/workbuddy/types.d.ts +179 -0
  83. package/lib/types/host/workbuddy/types.d.ts.map +1 -0
  84. package/lib/types/index.d.ts +35 -0
  85. package/lib/types/index.d.ts.map +1 -1
  86. package/lib/types/shared/workbuddy-contracts.d.ts +531 -0
  87. package/lib/types/shared/workbuddy-contracts.d.ts.map +1 -0
  88. package/package.json +6 -2
  89. package/presets/dispatch/README.md +57 -3
  90. package/presets/dispatch/agent.cordis.yml +42 -13
@@ -0,0 +1,214 @@
1
+ import type { WorkBuddyAccount, WorkBuddyAccountQuota, WorkBuddyMeter } from '../../shared/workbuddy-contracts.ts';
2
+ import { type WorkBuddyOutboundProduct } from './outbound-identity.ts';
3
+ import { PROVIDER_ID, PROVIDER_NAME } from './types.ts';
4
+ import { FileCredentialStore, type WorkBuddyCredentials } from './token-store.ts';
5
+ import type { WorkBuddyTokenIdentity } from './identity.ts';
6
+ import { type WorkBuddyModelEntry } from './model-catalog.ts';
7
+ export interface WorkBuddyRequestOptions {
8
+ fetchFn?: typeof fetch;
9
+ signal?: AbortSignal;
10
+ }
11
+ /**
12
+ * Which chat surface a call is, for the headers only that surface needs.
13
+ *
14
+ * Passing this opts the request into the conversation header group. It is
15
+ * deliberately a separate argument rather than a consequence of `sessionId`
16
+ * being present: the group belongs to the chat endpoint's channel check, and
17
+ * the config, billing, refresh and check-in surfaces keep the exact header set
18
+ * they already worked with.
19
+ */
20
+ export interface WorkBuddyChannelOptions {
21
+ /**
22
+ * Which client identity this request presents.
23
+ *
24
+ * Omitted means the line's shipped identity (`cli`). Only the chat call ever
25
+ * sets it: the config, billing and refresh surfaces are the ones measured to
26
+ * accept the CLI identity, and the choice exists for the chat fingerprint.
27
+ */
28
+ product?: WorkBuddyOutboundProduct;
29
+ /**
30
+ * Harness session the chat call belongs to.
31
+ *
32
+ * Omit it for a call with no conversation behind it (the connection-test
33
+ * probe). The group is still sent — the gateway requires it either way — but
34
+ * the scoped ids fall back to random values.
35
+ */
36
+ sessionId?: string;
37
+ /**
38
+ * Stable per-account key the conversation ids are derived from.
39
+ *
40
+ * Load-bearing for the pool: two accounts sending the SAME conversation id
41
+ * would present one device signing in as two identities, which is exactly
42
+ * what the gateway's channel check exists to catch. Naming the account here is
43
+ * what keeps every account's ids its own.
44
+ */
45
+ accountKey?: string;
46
+ /**
47
+ * One user send, as `userTurnKey` in `mapper.ts` derives it.
48
+ *
49
+ * Every tool step, retry and renewal replay of the SAME user turn keeps this
50
+ * value, so the gateway reads one send rather than N unrelated requests; the
51
+ * user's next message changes it. Absent or empty means "no turn to name" and
52
+ * degrades to a fresh id, which is the behaviour every non-chat surface keeps.
53
+ */
54
+ turnKey?: string;
55
+ /**
56
+ * This call goes to the Anthropic Messages surface.
57
+ *
58
+ * Adds its protocol headers. Deliberately explicit rather than inferred from
59
+ * the URL: the two surfaces want different header sets, and a call that
60
+ * guessed wrong would be refused for a reason the caller could have stated.
61
+ */
62
+ messages?: boolean;
63
+ }
64
+ /**
65
+ * Attribution and identity headers for one backend request.
66
+ *
67
+ * The gateway gates on this header set: the international deployment answers
68
+ * 401 without the CLI user agent and matching `Origin`/`Referer`, and both
69
+ * deployments read the account identity headers rather than deriving it from
70
+ * the bearer token alone.
71
+ *
72
+ * `channel` marks a request to the chat endpoint, which is the only surface
73
+ * whose channel check wants the {@link conversationHeaders} group.
74
+ */
75
+ export declare function workBuddyHeaders(credentials: Pick<WorkBuddyCredentials, 'accessToken' | 'domain' | 'uid' | 'enterpriseId' | 'backend'>, extra?: Record<string, string>, channel?: WorkBuddyChannelOptions): Record<string, string>;
76
+ /**
77
+ * Content coding a chat request's body is sent with.
78
+ *
79
+ * Measured: the official CLI's `GzipRequestProcessor` compresses each POST's
80
+ * body and sets `Content-Encoding: gzip`, and both builds turn that feature on.
81
+ * The saving is not marginal — a long agent conversation re-sends its whole
82
+ * history on every step, which plain-JSON uploads made bytes-on-the-wire the
83
+ * dominant cost of using this route.
84
+ *
85
+ * Only a chat completion is compressed: the same fetch also serves surfaces
86
+ * whose parser reads plain JSON (the images API answers 400 `code 11101`
87
+ * "invalid character '\\x1f'" to a gzipped body), so the caller opts in by
88
+ * calling this rather than every request getting it.
89
+ */
90
+ export declare const GZIP_CONTENT_ENCODING = "gzip";
91
+ /** One chat body, compact JSON, gzipped for the wire. */
92
+ export declare function gzipChatBody(body: string): Uint8Array;
93
+ /** Header set that declares one request's body coding. */
94
+ export declare function encodedBodyHeaders(encoding: string | undefined): Record<string, string>;
95
+ /**
96
+ * Refresh an access token against the endpoint the official IDE uses.
97
+ *
98
+ * The response carries a whole new `auth` block rather than just a token, so it
99
+ * is merged over the current one: fields the endpoint omits (the domain, the
100
+ * refresh lifetime) must survive, or the next refresh would lose its target.
101
+ */
102
+ export declare function refreshCredentials(credentials: WorkBuddyCredentials, options?: WorkBuddyRequestOptions): Promise<WorkBuddyCredentials>;
103
+ /** Public account facts for the settings card. */
104
+ export declare function accountFromCredentials(credentials: WorkBuddyCredentials): WorkBuddyAccount;
105
+ /**
106
+ * Read the signed-in account for a credential.
107
+ *
108
+ * The browser-authorization token response states the access token and the
109
+ * refresh token but **not** which account they belong to, so a login that
110
+ * relied on it alone could only key the account by the display name it also
111
+ * did not have — which is where the duplicate row and the `账号 1` label came
112
+ * from. The official client reads the account from this same endpoint right
113
+ * after polling the token; measured live, it returns the uid, the nickname,
114
+ * the UIN and the enterprise id for a valid bearer.
115
+ *
116
+ * A failure is not fatal to the sign-in: the token is usable, so the caller
117
+ * keeps the credential it has and lets the token's own claims fill in what
118
+ * they can.
119
+ */
120
+ export declare function fetchAccountIdentity(credentials: WorkBuddyCredentials, options?: WorkBuddyRequestOptions): Promise<WorkBuddyTokenIdentity | null>;
121
+ /**
122
+ * Parse one `/v3/config` payload's model list.
123
+ *
124
+ * Each entry carries the facts this route needs directly, so nothing is
125
+ * inferred from the model's name. Image-generation tools (`tags` naming
126
+ * `text-to-image`/`image-to-image`) are skipped: they are not chat models and
127
+ * carry no context window.
128
+ *
129
+ * `credits` is read here rather than left to the shipped table because a
130
+ * reachable gateway **replaces** that table entirely: a multiplier transcribed
131
+ * offline would be the only one a signed-in user ever saw, and it would stay
132
+ * wrong the moment the subscription repriced a model. A string this route cannot
133
+ * turn into a number (`''`, which `default-model` publishes, or a value a
134
+ * future gateway invents) leaves the field off the entry altogether instead of
135
+ * writing a plausible-looking `0` — see `parseWorkBuddyCreditMultiplier`.
136
+ */
137
+ export declare function parseConfigModels(payload: unknown, region: WorkBuddyCredentials['region']): WorkBuddyModelEntry[];
138
+ export declare function getCachedCatalog(): WorkBuddyModelEntry[];
139
+ export declare function clearCachedCatalog(): void;
140
+ /**
141
+ * Fetch the live catalog for one account.
142
+ *
143
+ * The gateway is the authority on capabilities, so a successful read replaces
144
+ * the shipped table entirely; a failure returns an empty list and the caller
145
+ * falls back, which keeps a first run usable before sign-in.
146
+ */
147
+ export declare function fetchConfigCatalog(credentials: WorkBuddyCredentials, options?: WorkBuddyRequestOptions): Promise<WorkBuddyModelEntry[]>;
148
+ /** Cached catalog with a TTL; a failed refresh keeps the previous snapshot.
149
+ *
150
+ * On the first cache miss of a process the persisted snapshot is rehydrated
151
+ * first, so a restart serves the previous listing without waiting on the
152
+ * network. An expired snapshot still answers immediately (stale) while a
153
+ * refresh runs in the background — the catalog is not fast-moving data, and
154
+ * waiting on an unreachable gateway turned a restart into the full discovery
155
+ * timeout on every model resolution.
156
+ */
157
+ export declare function loadConfigCatalog(credentials: WorkBuddyCredentials, options?: WorkBuddyRequestOptions & {
158
+ force?: boolean;
159
+ }): Promise<WorkBuddyModelEntry[]>;
160
+ /** Parse one `Capacity*` account record into the public quota shape. */
161
+ export interface ParsedBilling {
162
+ packageName: string | null;
163
+ totalCredits: number | null;
164
+ remainingCredits: number | null;
165
+ cycleUsedCredits: number | null;
166
+ cycleCredits: number | null;
167
+ cycleStartsAt: number | null;
168
+ cycleEndsAt: number | null;
169
+ }
170
+ /**
171
+ * Parse the billing payload's account list.
172
+ *
173
+ * The response nests as `data.Response.Data.Accounts[]`; each entry describes
174
+ * one purchased package with its own capacity counters. The first entry is the
175
+ * active subscription, and the totals are summed across all of them because a
176
+ * user can hold several packages at once.
177
+ */
178
+ export declare function parseBilling(payload: unknown): ParsedBilling;
179
+ /**
180
+ * Parse a cycle boundary.
181
+ *
182
+ * The service sends these as local-time strings without a zone
183
+ * (`2026-09-01 00:00:00`), which `Date.parse` reads as local time — the same
184
+ * reading the IDE's own panel uses, so the card agrees with it.
185
+ */
186
+ export declare function parseCycleTime(value: unknown): number | null;
187
+ /** Turn a parsed billing snapshot into the meters the card renders. */
188
+ export declare function billingMeters(billing: ParsedBilling): WorkBuddyMeter[];
189
+ /**
190
+ * The snapshot read most recently, whichever account it belongs to.
191
+ *
192
+ * Kept with a no-argument signature for backward compatibility; the page-level
193
+ * facts use {@link getCachedQuotaFor} instead, because they describe one
194
+ * account and must never be another account's numbers.
195
+ */
196
+ export declare function getCachedQuota(): WorkBuddyAccountQuota | undefined;
197
+ /**
198
+ * The snapshot remembered for one account, if it was read before.
199
+ *
200
+ * This is the lookup the account pool calls while it builds its summaries: a
201
+ * pure memory read, never a fetch. Passing no account keeps the pre-pool
202
+ * behavior of returning whatever was fetched last.
203
+ */
204
+ export declare function getCachedQuotaFor(accountId?: string | null): WorkBuddyAccountQuota | undefined;
205
+ export declare function clearCachedQuota(): void;
206
+ /**
207
+ * Read the account's credit allowance.
208
+ *
209
+ * Only one billing route exists here, so a failure is a real failure rather
210
+ * than a partially-answered snapshot: the caller decides whether to surface it.
211
+ */
212
+ export declare function fetchAccountQuota(store?: FileCredentialStore, fetchFn?: typeof fetch, force?: boolean, accountId?: string | null, hiddenAccountIds?: readonly string[]): Promise<WorkBuddyAccountQuota>;
213
+ export { PROVIDER_ID, PROVIDER_NAME };
214
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../src/host/workbuddy/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Recovery of tool calls the model PRINTED instead of emitting structurally.
3
+ *
4
+ * Measured upstream behaviour (and the reason workbuddy2api-hub has the same
5
+ * recovery, `wb_proxy.py` "Printed tool calls on the Chat Completions path"):
6
+ * a tools request sometimes answers with the call spelled out inside `content`
7
+ * — either the full-width DSML form
8
+ *
9
+ * <||DSML|| calls><||DSML|| invoke name="run_code">
10
+ * <||DSML|| parameter name="code">print(1)</||DSML|| parameter>
11
+ * </||DSML|| invoke></||DSML|| calls>
12
+ *
13
+ * or a hybrid the model mixes on its own: a plain opening tag closed by DSML
14
+ * tags, with the first parameter's close tag swallowed. Forwarded verbatim the
15
+ * client sees prose, finds no call, and the turn simply ends — reported by
16
+ * users as "it keeps stopping by itself".
17
+ *
18
+ * A structured `tool_calls` delta always wins; this only runs for text, which
19
+ * is why the recovery is a pure function over the accumulated content.
20
+ */
21
+ /** One recovered call, in the shape the mapper emits. */
22
+ export interface PrintedToolCall {
23
+ id: string;
24
+ name: string;
25
+ arguments: string;
26
+ }
27
+ /** A successful recovery: the calls plus the prose left behind. */
28
+ export interface PrintedRecovery {
29
+ calls: PrintedToolCall[];
30
+ clean: string;
31
+ }
32
+ /** Recover the full-width `<||DSML|| …>` spelling. */
33
+ export declare function parseDsmlToolCalls(text: string): PrintedRecovery | null;
34
+ /**
35
+ * Recover the hybrid spelling, driven by the `<parameter>` elements.
36
+ *
37
+ * Parameter elements are spelled the same way in both shapes and a parameter
38
+ * name is the one thing a call cannot do without, so they are the anchor; the
39
+ * nearest preceding opening tag that is neither a wrapper nor a parameter names
40
+ * the tool.
41
+ */
42
+ export declare function parseLeakedToolCalls(text: string): PrintedRecovery | null;
43
+ /** Recover a printed call under either spelling. */
44
+ export declare function recoverPrintedToolCalls(text: string): PrintedRecovery | null;
45
+ /**
46
+ * Index from which a streaming text buffer must be held back.
47
+ *
48
+ * The earliest `<` that either opens a recognisable tag or could still grow
49
+ * into one (a delta may split `<parameter` across frames), or -1 when the
50
+ * whole buffer is safe to emit as prose.
51
+ */
52
+ export declare function printedCallHoldIndex(text: string): number;
53
+ /** Whether a held buffer has already reached a closing tag. */
54
+ export declare function printedCallLooksComplete(text: string): boolean;
55
+ //# sourceMappingURL=dsml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dsml.d.ts","sourceRoot":"","sources":["../../../../src/host/workbuddy/dsml.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,yDAAyD;AACzD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,mEAAmE;AACnE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;CACf;AA2CD,sDAAsD;AACtD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAiBvE;AAiBD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAyFzE;AAED,oDAAoD;AACpD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAE5E;AAgBD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASzD;AAED,+DAA+D;AAC/D,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9D"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Local quota guardrails for the WorkBuddy line.
3
+ *
4
+ * The subscription meters an account × model over a 24h window (429 `code
5
+ * 6004`), but it only says so AFTER the window is gone — and on the shared
6
+ * account that window is minutes of a large model or a day of a small one. These
7
+ * guardrails stop serving BEFORE that happens, so an exhausted window never has
8
+ * to be discovered by a user staring at a 429.
9
+ *
10
+ * Three of the four guardrails workbuddy2api-hub ships are reproduced here, and
11
+ * all three are EXACT — they read numbers this plugin already has:
12
+ *
13
+ * reserveCredits stop the account when its remaining credits fall below N
14
+ * (the billing snapshot already reports the remainder)
15
+ * dailyTokens stop the account for the rest of the local day at N tokens
16
+ * modelDailyTokens stop ONE model for the rest of the local day at N tokens
17
+ *
18
+ * The fourth one there — a daily CREDITS budget — is deliberately NOT
19
+ * reproduced: a request's credit cost is not on the wire this route reads, and
20
+ * deriving it from the catalog multiplier × tokens would invent a number that
21
+ * silently diverges from the bill. A guardrail that lies is worse than none.
22
+ *
23
+ * `0` means OFF for every field, and the default is all-off: an install that
24
+ * never opens this setting behaves exactly as before.
25
+ */
26
+ import type { WorkBuddyGuardrailsDto } from '../../shared/workbuddy-contracts.ts';
27
+ /**
28
+ * The three exact guardrails.
29
+ *
30
+ * The shape has ONE definition — the shared wire contract — so the card and the
31
+ * enforcement can never disagree about a field name.
32
+ */
33
+ export type WorkBuddyGuardrails = WorkBuddyGuardrailsDto;
34
+ /** Shipped default: every guardrail off. */
35
+ export declare const DEFAULT_GUARDRAILS: WorkBuddyGuardrails;
36
+ /** Narrow a stored or patched value into guardrails; anything unusable means off. */
37
+ export declare function normalizeGuardrails(value: unknown): WorkBuddyGuardrails;
38
+ /** Whether any guardrail is armed; a no-op check must stay cheap. */
39
+ export declare function guardrailsArmed(guardrails: WorkBuddyGuardrails): boolean;
40
+ /**
41
+ * The local calendar day a counter belongs to.
42
+ *
43
+ * Local, not UTC, because the reset the user reasons about is their own
44
+ * midnight — the same choice this line makes for the CN check-in day.
45
+ */
46
+ export declare function localDayKey(now?: number): string;
47
+ /** Per-account counters for one day. */
48
+ export interface UsageLedgerEntry {
49
+ tokens: number;
50
+ perModel: Record<string, number>;
51
+ }
52
+ /** Where the local usage counters live. */
53
+ export declare function usageLedgerPath(): string;
54
+ /**
55
+ * Token counters per account per local day.
56
+ *
57
+ * Deliberately a small persisted document rather than an accounting system: it
58
+ * exists to answer one question ("has this account / model used its day?") and a
59
+ * restart must not forget it, or a guardrail would reset by restarting DSH.
60
+ */
61
+ export declare class WorkBuddyUsageLedger {
62
+ private readonly filePath;
63
+ private readonly now;
64
+ private document;
65
+ private loaded;
66
+ private writing;
67
+ constructor(filePath?: string, now?: () => number);
68
+ /** Read the counters once; a missing or unreadable file starts a fresh day. */
69
+ load(): Promise<void>;
70
+ /** Drop yesterday's counters the moment a read notices the day changed. */
71
+ private rollIfNewDay;
72
+ /** Tokens one account has used today, across models. */
73
+ tokensToday(accountId: string): number;
74
+ /** Tokens one account has used today on one model. */
75
+ modelTokensToday(accountId: string, model: string): number;
76
+ /** Today's counters, for the settings card and for tests. */
77
+ snapshot(): Readonly<Record<string, UsageLedgerEntry>>;
78
+ /**
79
+ * Add one request's tokens.
80
+ *
81
+ * Serialized through a promise chain: two concurrent requests on different
82
+ * accounts would otherwise read the same document, add their own counts and
83
+ * write, losing one of them.
84
+ */
85
+ record(accountId: string, model: string, tokens: number): Promise<void>;
86
+ private persist;
87
+ }
88
+ /** Why an account may not serve a request. */
89
+ export type GuardrailReason = 'reserve-credits' | 'daily-tokens' | 'model-daily-tokens';
90
+ /** The verdict for one account on one model. */
91
+ export type GuardrailVerdict = {
92
+ allowed: true;
93
+ } | {
94
+ allowed: false;
95
+ reason: GuardrailReason;
96
+ detail: string;
97
+ };
98
+ /**
99
+ * Decide whether one account may serve one model right now.
100
+ *
101
+ * Pure on purpose: every number it reads is passed in, so the decision can be
102
+ * tested without a pool, a clock or a network.
103
+ */
104
+ export declare function evaluateGuardrails(input: {
105
+ guardrails: WorkBuddyGuardrails;
106
+ tokensToday: number;
107
+ modelTokensToday: number;
108
+ /** Remaining credits from the billing snapshot; null when never read. */
109
+ remainingCredits: number | null | undefined;
110
+ }): GuardrailVerdict;
111
+ //# sourceMappingURL=guardrails.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../../../../src/host/workbuddy/guardrails.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAKH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAA;AAEjF;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,sBAAsB,CAAA;AAExD,4CAA4C;AAC5C,eAAO,MAAM,kBAAkB,EAAE,mBAIhC,CAAA;AAOD,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAQvE;AAED,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,UAAU,EAAE,mBAAmB,GAAG,OAAO,CAExE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,SAAa,GAAG,MAAM,CAKpD;AAED,wCAAwC;AACxC,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACjC;AAQD,2CAA2C;AAC3C,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;;;GAMG;AACH,qBAAa,oBAAoB;IAM7B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG;IANtB,OAAO,CAAC,QAAQ,CAAmE;IACnF,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,OAAO,CAAmC;gBAG/B,QAAQ,SAAoB,EAC5B,GAAG,GAAE,MAAM,MAAyB;IAGvD,+EAA+E;IACzE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqC3B,2EAA2E;IAC3E,OAAO,CAAC,YAAY;IAMpB,wDAAwD;IACxD,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAKtC,sDAAsD;IACtD,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAK1D,6DAA6D;IAC7D,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAKtD;;;;;;OAMG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAY/D,OAAO;CAMtB;AAED,8CAA8C;AAC9C,MAAM,MAAM,eAAe,GAAG,iBAAiB,GAAG,cAAc,GAAG,oBAAoB,CAAA;AAEvF,gDAAgD;AAChD,MAAM,MAAM,gBAAgB,GACxB;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjB;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/D;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IACxC,UAAU,EAAE,mBAAmB,CAAA;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,EAAE,MAAM,CAAA;IACxB,yEAAyE;IACzE,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;CAC5C,GAAG,gBAAgB,CA4BnB"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * WorkBuddy account identity, resolved from the credential itself.
3
+ *
4
+ * The defect this exists to prevent: {@link workBuddyAccountId} falls back to
5
+ * `nickname` when no uid is present, and the browser-login response carries the
6
+ * display name but no uid — so signing in to an account that the IDE already
7
+ * holds produced `intl:<e-mail>` from the login while the very same account
8
+ * scanned from the IDE's own `*.info` file produced `intl:<uuid>`. Two ids
9
+ * meant two pool rows, two entries in `/accounts` and two cards for one
10
+ * account, which is exactly what users reported as "the same account appears
11
+ * twice". The same fallback is why a credential that carried no display name at
12
+ * all was labelled "账号 1".
13
+ *
14
+ * A display name is not an identity. The access token is a Keycloak JWT whose
15
+ * `sub` is the account's stable uid on both deployments (measured against a
16
+ * live domestic and international token), so the identity is read from there
17
+ * and the file's or response's own fields are used only for what the token
18
+ * cannot supply.
19
+ */
20
+ import type { WorkBuddyCredentials } from './token-store.ts';
21
+ /** Identity facts a token or a login response can state about one account. */
22
+ export interface WorkBuddyTokenIdentity {
23
+ uid?: string;
24
+ nickname?: string;
25
+ uin?: string;
26
+ enterpriseId?: string;
27
+ }
28
+ /**
29
+ * Decode a JWT's payload without verifying it.
30
+ *
31
+ * Nothing here is a security decision: the claims only name the account so the
32
+ * plugin can tell two credentials apart. The token is still validated by the
33
+ * gateway on every request, so an unverified claim cannot grant anything — it
34
+ * can at most make two accounts look alike, which the request path would then
35
+ * reject on its own.
36
+ *
37
+ * Returns null for an opaque or malformed token rather than throwing: a
38
+ * non-JWT access token is a legitimate shape this route must keep working with.
39
+ */
40
+ export declare function decodeAccessTokenClaims(accessToken: string): Record<string, unknown> | null;
41
+ /** Read the identity facts one decoded claim set states. */
42
+ export declare function identityFromClaims(claims: Record<string, unknown>): WorkBuddyTokenIdentity;
43
+ /** Identity facts an access token states, or an empty object when it states none. */
44
+ export declare function identityFromAccessToken(accessToken: string): WorkBuddyTokenIdentity;
45
+ /**
46
+ * Whether a credential names its own account.
47
+ *
48
+ * {@link workBuddyAccountId} falls back to the auth domain, which is a property
49
+ * of the deployment rather than of the account: two accounts on one deployment
50
+ * would share it. A credential that states no uid, UIN or display name
51
+ * therefore has no identity to derive, and a caller holding a meaningful key
52
+ * already — a row the pool stored under one — must keep it instead of
53
+ * replacing it with a domain-derived key.
54
+ */
55
+ export declare function hasStableIdentity(credentials: Pick<WorkBuddyCredentials, 'uid' | 'uin' | 'nickname'>): boolean;
56
+ /**
57
+ * Backfill a credential's identity from its own access token.
58
+ *
59
+ * The token's `sub` is the account's uid, so it wins over a stored value that
60
+ * disagrees with it: that stored value can only have come from the login path
61
+ * that promoted a display name into `uid`, which is the record this function
62
+ * exists to heal. The displaced value is kept as the nickname when nothing else
63
+ * names the account, so a healed row still reads as something a person
64
+ * recognizes instead of an opaque uuid.
65
+ *
66
+ * Every other field is only filled in when the credential does not state it:
67
+ * what the IDE's file or the login response already records is not second
68
+ * guessed from a token.
69
+ */
70
+ export declare function withResolvedIdentity<T extends WorkBuddyCredentials>(credentials: T): T;
71
+ //# sourceMappingURL=identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../../../src/host/workbuddy/identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAE5D,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACrC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAaD;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAa3F;AAED,4DAA4D;AAC5D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,sBAAsB,CAoB1F;AAED,qFAAqF;AACrF,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,GAAG,sBAAsB,CAGnF;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,IAAI,CAAC,oBAAoB,EAAE,KAAK,GAAG,KAAK,GAAG,UAAU,CAAC,GAAG,OAAO,CAI9G;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,oBAAoB,EAAE,WAAW,EAAE,CAAC,GAAG,CAAC,CA2BtF"}