dsh-connect-workbuddy 4.14.1 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts CHANGED
@@ -574,6 +574,27 @@ declare class WorkBuddyCredentialStore {
574
574
  }>;
575
575
  }
576
576
  //#endregion
577
+ //#region src/image-gen.d.ts
578
+ /**
579
+ * 产品配置 `data.models` 里的一个媒体模型条目。
580
+ *
581
+ * 实测上游只给三个字段(`{"id":"hunyuan-image-alpha","name":"Hunyuan Image Alpha",
582
+ * "tags":["text-to-image"]}`),没有 credits、没有上下文长度。不要在这里补
583
+ * 上游没有的字段——那正是「不猜」红线的反面。
584
+ */
585
+ interface WorkBuddyMediaModel {
586
+ readonly id: string;
587
+ readonly name: string;
588
+ readonly tags: readonly string[];
589
+ }
590
+ /** 上游成功响应的归一化结果。`url` 与 `b64` 至少有一个。 */
591
+ interface WorkBuddyImageOutcome {
592
+ readonly url?: string;
593
+ readonly b64?: string;
594
+ readonly created?: number;
595
+ readonly credit?: number;
596
+ }
597
+ //#endregion
577
598
  //#region src/promo.d.ts
578
599
  /**
579
600
  * WorkBuddy promotion & tier badges — the upstream's own "this model is on
@@ -747,6 +768,59 @@ interface WorkBuddyPromo {
747
768
  priority: number;
748
769
  }
749
770
  //#endregion
771
+ //#region src/busy.d.ts
772
+ /**
773
+ * WorkBuddy model load — the "繁忙程度" line the WorkBuddy client draws under a
774
+ * model, read from the upstream's own probe and rendered next to the model name.
775
+ *
776
+ * WHY A SEPARATE REQUEST, AND WHY IT COSTS ALMOST NOTHING. Load is NOT part of
777
+ * the model directory: `/v3/config` lists models and prices, and says nothing
778
+ * about how busy each one is right now. The app asks a dedicated probe —
779
+ * `POST /v2/chat/busy { models: [...] }` — and so does this plugin, exactly once
780
+ * per model-picker refresh, batched at the upstream's own limit of 50 ids per
781
+ * call. The probe is read-only and free (no credits, no account mutation):
782
+ * measured 2026-10-21 on the CN gateway, HTTP 200 with
783
+ *
784
+ * {"code":0,"msg":"OK","requestId":"…","data":[
785
+ * {"model":"hy3","busy_level":"busy"},
786
+ * {"model":"deepseek-v4.1-flash","busy_level":"congested"}]}
787
+ *
788
+ * IDENTICAL ON EVERY CN HOST AND UNDER EITHER CLIENT IDENTITY. `copilot.tencent.com`,
789
+ * `www.workbuddy.cn` and `www.codebuddy.cn` answer byte-for-byte the same list,
790
+ * and so do the CLI user agent (`CLI/2.63.2 CodeBuddy/2.63.2`) and the app's
791
+ * product identity (`WorkBuddy/5.8.3 WorkBuddy/5.8.3 CLI/2.161.0`). Unlike
792
+ * `/v3/config`, this endpoint is not channel-sensitive, so the plugin asks it as
793
+ * the CLI agent it already is on the chat path — no product identity needed.
794
+ *
795
+ * THE LEVELS ARE THE APP'S OWN ENUM, and anything outside it is `unknown`:
796
+ * `idle` / `normal` / `busy` / `congested` / `unavailable`. The app's own words
797
+ * (from its `im-channels` bundle, `chatInput.modelSelector.busy.*`) are
798
+ * 空闲 / 轻微拥堵 / 极度拥堵 / 不可用; English reads Idle / Slightly congested /
799
+ * Extremely congested / Unavailable.
800
+ *
801
+ * WHY THE LABELS LIVE HERE AND NOT IN THE UPSTREAM CLIENT. The probe returns a
802
+ * machine level, never text: the same response is drawn differently by the app
803
+ * (an icon for `congested`, a word for the rest), so the rendering decision
804
+ * belongs with the renderer. This module keeps the words, the tone, and the
805
+ * "which levels are worth drawing at all" rule in one place.
806
+ *
807
+ * NOTHING HERE IS PERSISTED. Load is a fact about THIS MINUTE: a cached level
808
+ * that survives a restart is a lie, and the model directory is saved to disk
809
+ * while a level must not be. So the level is attached to the catalog entry at
810
+ * refresh time and nowhere else — see `attachBusyLevels` and `src/index.ts`.
811
+ *
812
+ * @module dsh-connect-workbuddy/busy
813
+ */
814
+ /**
815
+ * One model's load, in the upstream's vocabulary.
816
+ *
817
+ * `unknown` is the plugin's own value, not the upstream's: it is what an
818
+ * unusable entry becomes (a missing level, a level outside the enum, a model the
819
+ * probe did not answer for). It is deliberately distinct from `normal` — "we
820
+ * were told nothing" must not be drawn as "all good".
821
+ */
822
+ type WorkBuddyBusyLevel = 'idle' | 'normal' | 'busy' | 'congested' | 'unavailable' | 'unknown';
823
+ //#endregion
750
824
  //#region src/upstream.d.ts
751
825
  /** WorkBuddy region selected by the credential's login domain. */
752
826
  type WorkBuddyRegion = 'cn' | 'global';
@@ -802,7 +876,27 @@ type UpstreamErrorKind = 'hard_credit' |
802
876
  * later and each attempt teaches the pool a false fact about a credential
803
877
  * that was never the problem.
804
878
  */
805
- 'channel_reject' | 'not_found' | 'server' | 'client';
879
+ 'channel_reject' |
880
+ /**
881
+ * The upstream put this request in a queue instead of running it: business
882
+ * codes `6020` (waiting, with a position), `6021` (queue full), `6022` (this
883
+ * user already holds the maximum). See `src/queue.ts`.
884
+ *
885
+ * Its own kind because it is the ONE failure where the credential was fine,
886
+ * the request was fine, and the server already accepted the work — telling the
887
+ * user "请求失败" or rotating accounts is wrong on both counts. A queued
888
+ * request follows the MODEL and the shared queue, not the account, so another
889
+ * account is handed the same position: NOT failover-worthy (see
890
+ * `isFailoverWorthy` in the shim).
891
+ *
892
+ * Classified BEFORE the plain status branches: the queue condition arrives as
893
+ * a business code and the HTTP status it rides on is incidental (observed on
894
+ * 200-shaped envelopes and on 429/500). Without this member it fell through to
895
+ * `server`/`soft_rate`, both of which DO fail over — so hitting a queue walked
896
+ * the whole account pool, burning a real request per account, before saying
897
+ * "you are #8777 in line".
898
+ */
899
+ 'model_queue' | 'not_found' | 'server' | 'client';
806
900
  /** Reasoning capability as the upstream catalog declares it. */
807
901
  interface WorkBuddyReasoning {
808
902
  supportedEfforts?: readonly string[];
@@ -864,6 +958,16 @@ interface WorkBuddyUpstreamModel {
864
958
  * not where the document was read. See `src/promo.ts`.
865
959
  */
866
960
  promos?: readonly WorkBuddyPromo[];
961
+ /**
962
+ * How busy this model is RIGHT NOW, from the upstream's own probe
963
+ * (`POST /v2/chat/busy`, see `src/busy.ts`).
964
+ *
965
+ * Deliberately NOT part of the catalog document, and deliberately not
966
+ * persisted: the directory is a fact that survives a restart, a load level is
967
+ * a fact about the minute it was measured. It is stamped onto the in-memory
968
+ * catalog entry at refresh time and disappears with the process.
969
+ */
970
+ busyLevel?: WorkBuddyBusyLevel;
867
971
  }
868
972
  /** One billing package as the upstream returns it, dates already parsed. */
869
973
  interface WorkBuddyCreditPackage {
@@ -1231,6 +1335,48 @@ declare class WorkBuddyUpstreamClient {
1231
1335
  * one step upstream of it).
1232
1336
  */
1233
1337
  fetchModels(credential: WorkBuddyCredential, signal?: AbortSignal, keepModelIds?: readonly string[]): Promise<readonly WorkBuddyUpstreamModel[]>;
1338
+ /**
1339
+ * Read how busy each model is right now, from the upstream's own probe.
1340
+ *
1341
+ * Batched at the upstream's ceiling (`WORKBUDDY_BUSY_BATCH_SIZE`), because the
1342
+ * probe takes a list of ids and answers only about the ids it was given.
1343
+ *
1344
+ * Throws on anything that is not a clean `code: 0`, like every other reader in
1345
+ * this class — the CALLER decides that a failed load probe is survivable and
1346
+ * catches it. Nothing here is persisted: the level belongs to the refresh that
1347
+ * asked for it.
1348
+ */
1349
+ fetchModelBusy(credential: WorkBuddyCredential, modelIds: readonly string[], signal?: AbortSignal): Promise<Map<string, WorkBuddyBusyLevel>>;
1350
+ /**
1351
+ * 读该账号所属区的产品配置,挑出带 `text-to-image` 标签的媒体模型。
1352
+ *
1353
+ * 走的是和 {@link fetchModels} 同一条 `/v3/config`(国内区与国际区都答),
1354
+ * 区别只在这里保留 `data.models` 的媒体条目、而不是投影成 CLI 聊天目录。
1355
+ * 国内区实测 62 条模型里只有 `hunyuan-image-alpha` 带 `text-to-image`;
1356
+ * 国际区一条都没有 —— 所以本方法对国际区返回空数组,而不是编一个模型出来。
1357
+ *
1358
+ * 这是一次 EXTRA 读取,不是「复用目录那一次」:`fetchModels` 的结果被投影成
1359
+ * 聊天目录就丢掉了媒体标签,所以这里必须再读一遍同一份文档。它是只读 GET、
1360
+ * 不花积分,且工具侧有 5 分钟缓存,但「零新增请求」的说法不成立,别那么写。
1361
+ * 国内区与 {@link fetchModels} 一样走 `/v3` → `/v2` 降级:上游一旦不答
1362
+ * `/v3`,聊天目录会降级而这里不能变成一个原始异常。
1363
+ */
1364
+ fetchMediaModels(credential: WorkBuddyCredential, signal?: AbortSignal): Promise<readonly WorkBuddyMediaModel[]>;
1365
+ /**
1366
+ * 发一次文生图请求。
1367
+ *
1368
+ * 端点、请求体、响应解析全部照抄官方 CLI 的 ImageService(见 `image-gen.ts`
1369
+ * 的模块注释)。实测单张 5.71 credits,产物是 24 小时有效的 COS 签名 URL。
1370
+ *
1371
+ * 失败分两层:HTTP 非 2xx 交给 {@link describeImageFailure} 翻成带 code 的
1372
+ * 错误(401/403 提示重新登录、404 提示该区没有生图路由、429 提示限流);
1373
+ * HTTP 200 但信封 `code !== 0` 由 {@link parseImageResponse} 处理。
1374
+ */
1375
+ generateImage(credential: WorkBuddyCredential, request: {
1376
+ readonly model: string;
1377
+ readonly prompt: string;
1378
+ readonly size?: string;
1379
+ }, signal?: AbortSignal): Promise<WorkBuddyImageOutcome>;
1234
1380
  /** Query today's check-in status without changing account state. */
1235
1381
  fetchCheckinStatus(credential: WorkBuddyCredential): Promise<WorkBuddyCheckinStatus>;
1236
1382
  /**
@@ -2938,6 +3084,15 @@ type WorkBuddyWebUsage =
2938
3084
  * "关闭不了" report.
2939
3085
  */
2940
3086
  showCreditsInMainUi?: boolean;
3087
+ /**
3088
+ * Whether the model may call the image-generation tool here (opt-in).
3089
+ *
3090
+ * Travels on the Host's answer for the same reason as the fields around it,
3091
+ * and matters more: a checkbox rendering a stale value would let the user
3092
+ * believe the spending capability was ON when it is off, or leave it on
3093
+ * after a switch-off that never landed.
3094
+ */
3095
+ imageToolEnabled?: boolean;
2941
3096
  /** WHERE this region's readout sits (issue #62); absent means `composer`. */
2942
3097
  creditPosition?: WorkBuddyCreditPosition;
2943
3098
  message?: string;
@@ -2974,6 +3129,15 @@ type WorkBuddyWebUsage =
2974
3129
  * settings mirror.
2975
3130
  */
2976
3131
  showCreditsInMainUi?: boolean;
3132
+ /**
3133
+ * Whether the model may call the image-generation tool here (opt-in).
3134
+ *
3135
+ * Travels on the Host's answer for the same reason as the fields around it,
3136
+ * and matters more: a checkbox rendering a stale value would let the user
3137
+ * believe the spending capability was ON when it is off, or leave it on
3138
+ * after a switch-off that never landed.
3139
+ */
3140
+ imageToolEnabled?: boolean;
2977
3141
  /** WHERE this region's readout sits (issue #62); absent means `composer`. */
2978
3142
  creditPosition?: WorkBuddyCreditPosition;
2979
3143
  source?: 'desktop' | 'dsh';
@@ -3419,6 +3583,14 @@ interface WorkBuddyStatusRouteOptions {
3419
3583
  * existed, so a Host that predates it renders exactly as it used to.
3420
3584
  */
3421
3585
  creditPosition?(region: WorkBuddyRegion): WorkBuddyCreditPosition;
3586
+ /**
3587
+ * Whether the model may call the image-generation tool in this region.
3588
+ *
3589
+ * Optional, and absent reads as OFF — the opposite of the fields above. This
3590
+ * one authorizes the MODEL to spend credits on its own, so an older Host that
3591
+ * cannot answer must not be read as consent.
3592
+ */
3593
+ imageToolEnabled?(region: WorkBuddyRegion): boolean;
3422
3594
  /**
3423
3595
  * Send one real-volume request to each named model of a region and report what
3424
3596
  * came back.
@@ -3824,6 +3996,15 @@ interface WorkBuddyRegionState {
3824
3996
  * written before this switch existed keeps whatever the default was.
3825
3997
  */
3826
3998
  showCreditsInMainUi?: boolean;
3999
+ /**
4000
+ * Whether the model may call the image-generation tool here (opt-IN).
4001
+ *
4002
+ * The only switch in this interface where an ABSENT value means OFF. Every
4003
+ * other flag here governs something the user can undo by clicking again;
4004
+ * this one authorizes the model to spend real credits on its own, so an
4005
+ * upgrade — or a config whose value could not be read — must leave it off.
4006
+ */
4007
+ imageToolEnabled?: boolean;
3827
4008
  /**
3828
4009
  * WHERE this region's credit readout sits (issue #62).
3829
4010
  *