dsh-connect-workbuddy 1.4.0 → 2.0.1

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
@@ -33,8 +33,22 @@ interface WorkBuddyAuthStatus {
33
33
  interface WorkBuddyStoreOptions {
34
34
  /** Explicit desktop auth-file path, overriding env and platform defaults. */
35
35
  desktopPath?: string;
36
- /** Explicit plugin-owned copy path, defaulting under `$DSH_HOME`. */
36
+ /**
37
+ * Explicit plugin-owned copy path, overriding the per-region default.
38
+ * Injectable so the copy/refresh cycle is testable without a real machine.
39
+ */
37
40
  ownPath?: string;
41
+ /**
42
+ * Region this store serves. When set, only credentials whose login domain
43
+ * maps to this region are discovered, selected, or refreshed — the two
44
+ * regions' stores run side by side without seeing each other's accounts.
45
+ */
46
+ region?: WorkBuddyRegion;
47
+ /**
48
+ * Legacy single-copy path read as a migration source; defaults to the
49
+ * pre-dual-provider location. Injectable for tests.
50
+ */
51
+ legacyOwnPath?: string;
38
52
  /**
39
53
  * Auth directories to scan, overriding the platform defaults. Injectable so
40
54
  * the multi-account scan is testable without touching a real machine.
@@ -58,12 +72,22 @@ interface WorkBuddyAccountChoice {
58
72
  filePath: string;
59
73
  selected: boolean;
60
74
  }
61
- /** Basename of the plugin-owned credential copy inside the Harness home. */
75
+ /** Legacy single-copy basename (pre-dual-provider); kept as migration source. */
62
76
  declare const WORKBUDDY_AUTH_FILENAME = ".workbuddy-auth.json";
63
77
  /** Env variable that overrides the desktop auth-file location. */
64
78
  declare const WORKBUDDY_AUTH_FILE_ENV = "WORKBUDDY_AUTH_FILE";
65
- /** Plugin-owned copy path inside the Harness home. */
66
- declare function workbuddyOwnAuthPath(): string;
79
+ /**
80
+ * Plugin-owned copy path for one region inside the Harness home. Each
81
+ * region's store refreshes into its own file so two simultaneously signed-in
82
+ * regions never overwrite each other's refreshed token.
83
+ */
84
+ declare function workbuddyOwnAuthPath(region: WorkBuddyRegion): string;
85
+ /**
86
+ * Pre-dual-provider single-copy path. Still read as a migration source (a
87
+ * legacy credential serves the region it belongs to until that region's own
88
+ * first refresh writes the per-region file), and removed by `logout`.
89
+ */
90
+ declare function legacyWorkbuddyOwnAuthPath(): string;
67
91
  /**
68
92
  * Platform-default directories holding the WorkBuddy desktop app's auth file.
69
93
  *
@@ -110,12 +134,34 @@ declare function workbuddyAccountId(credential: Pick<WorkBuddyCredential, 'uin'
110
134
  declare class WorkBuddyCredentialStore {
111
135
  private readonly refresh;
112
136
  private readonly refreshMarginMs;
113
- private readonly ownPath;
137
+ private readonly region;
138
+ private readonly ownPathExplicit;
139
+ private readonly legacyOwnPath;
140
+ private readonly legacyOwnPathExplicit;
114
141
  private readonly authDirs;
115
142
  private desktopPathOverride;
116
143
  private accountId;
117
144
  private inflight;
118
145
  constructor(options: WorkBuddyStoreOptions);
146
+ /** Whether a credential's login domain belongs to this store's region. */
147
+ private matchesRegion;
148
+ /**
149
+ * The path this store refreshes into: the per-region file for a
150
+ * region-scoped store, the legacy single file otherwise, or an explicitly
151
+ * injected path in tests.
152
+ */
153
+ ownAuthPath(): string;
154
+ /**
155
+ * Every plugin-owned copy to read, most preferred first. A region-scoped
156
+ * store reads the legacy single copy as its migration source (readAll's
157
+ * region filter drops it when it carries the other region's credential); an
158
+ * unscoped store reads everything so diagnostics see both regions.
159
+ *
160
+ * With an explicitly injected own path the legacy source is read ONLY when
161
+ * it was injected too — a test that pins one file must not accidentally see
162
+ * the real machine's legacy copy.
163
+ */
164
+ private ownCandidates;
119
165
  /** Repoint the desktop file or directory; applies on the next read. */
120
166
  setDesktopPath(path: string | undefined): void;
121
167
  /** Select an account by id; tokens stay outside settings. */
@@ -126,8 +172,6 @@ declare class WorkBuddyCredentialStore {
126
172
  private resolveDesktopCandidates;
127
173
  /** The resolved desktop auth-file path, for diagnostics. */
128
174
  desktopAuthPath(): string | undefined;
129
- /** The plugin-owned copy path, for diagnostics. */
130
- ownAuthPath(): string;
131
175
  /**
132
176
  * Every auth file to scan: the live file plus the timestamped backups
133
177
  * WorkBuddy leaves beside it.
@@ -146,6 +190,10 @@ declare class WorkBuddyCredentialStore {
146
190
  /**
147
191
  * Read every local credential, deduplicated by account id. Files are
148
192
  * probed newest-first, so the first entry for an account is its freshest.
193
+ *
194
+ * A region-scoped store sees only its own region's credentials: the other
195
+ * region's accounts are invisible to selection, refresh, and status alike,
196
+ * which is what keeps the two regions' providers from cross-billing.
149
197
  */
150
198
  private readAll;
151
199
  /**
@@ -164,12 +212,22 @@ declare class WorkBuddyCredentialStore {
164
212
  resolve(): Promise<WorkBuddyCredential>;
165
213
  /** Read-only sign-in summary; never refreshes and never throws. */
166
214
  status(): Promise<WorkBuddyAuthStatus>;
167
- /** Remove the plugin-owned copy; the desktop files are untouched. */
215
+ /**
216
+ * Remove every plugin-owned copy this store could read (per-region file,
217
+ * legacy single file, and their lock siblings); the desktop files are
218
+ * untouched. A region store's logout therefore also clears the legacy
219
+ * migration source — deliberate: `logout` is the user's "forget what the
220
+ * plugin stored" action, not a per-account toggle.
221
+ */
168
222
  logout(): Promise<void>;
169
223
  private needsRefresh;
170
224
  private refreshNow;
171
225
  private saveOwn;
172
- private readOwn;
226
+ /**
227
+ * Every readable plugin-owned copy, in candidate order; absent or corrupt
228
+ * files are skipped rather than propagated.
229
+ */
230
+ private readOwns;
173
231
  /** Whether any candidate file exists as a regular file; diagnostics only. */
174
232
  desktopFilePresent(): Promise<boolean>;
175
233
  }
@@ -264,7 +322,18 @@ type WorkBuddyChatResult = {
264
322
  };
265
323
  /** Classify an upstream failure from its HTTP status and body excerpt. */
266
324
  declare function classifyUpstreamError(status: number, body: string): UpstreamErrorKind;
267
- /** Region for a login domain; an empty domain means CN (matching upstream tooling). */
325
+ /**
326
+ * Region for a login domain; an empty domain means CN (matching upstream tooling).
327
+ *
328
+ * The international product is reachable under TWO brand domains: the WorkBuddy
329
+ * AI desktop app signs in at `workbuddy.ai`, while the CodeBuddy CLI signs the
330
+ * same international account in at `codebuddy.ai` (verified against a real
331
+ * credential file, issue #4). Both are served by the same gateway stack — a
332
+ * read-only probe shows `/v3/config` answering HTTP 200 with the same JSON
333
+ * envelope on both hosts — so both classify as `global`. Missing the
334
+ * `codebuddy.ai` spelling sent those tokens to the CN gateway, which rejected
335
+ * them at the openresty layer with an HTML 401.
336
+ */
268
337
  declare function regionOf(domain: string): WorkBuddyRegion;
269
338
  /**
270
339
  * Normalize an OpenAI chat-completions body for the WorkBuddy upstream:
@@ -355,6 +424,12 @@ declare function deriveCatalog(catalog: readonly WorkBuddyModelInfo[], enabled:
355
424
  /** Mutable catalog shared by the shim's `/v1/models` and the adapter. */
356
425
  declare class WorkBuddyCatalog {
357
426
  private models;
427
+ /**
428
+ * @param region Seeds the static fallback for this region; each region's
429
+ * provider must never serve the other region's roster before its first
430
+ * live refresh lands.
431
+ */
432
+ constructor(region?: 'cn' | 'global');
358
433
  /** Current entries; the fallback list until the upstream answer lands. */
359
434
  current(): readonly WorkBuddyModelInfo[];
360
435
  /** Replace the list; callers invalidate their adapter snapshot after this. */
@@ -397,8 +472,16 @@ interface WorkBuddyShimOptions {
397
472
  declare function createWorkBuddyShim(options: WorkBuddyShimOptions): WorkBuddyShim;
398
473
  //#endregion
399
474
  //#region src/adapter.d.ts
400
- /** Provider route this bundle owns. */
475
+ /** Provider route this bundle owns for the domestic (CN) gateway. */
401
476
  declare const WORKBUDDY_PROVIDER = "workbuddy";
477
+ /** Provider route this bundle owns for the international gateway. */
478
+ declare const WORKBUDDY_GLOBAL_PROVIDER = "workbuddy-global";
479
+ /** The provider id each region registers as. */
480
+ declare const WORKBUDDY_PROVIDERS: Readonly<Record<WorkBuddyRegion, string>>;
481
+ /** Region a provider route id belongs to. */
482
+ declare function regionOfProvider(provider: string): WorkBuddyRegion | undefined;
483
+ /** Human-readable provider name, shown in the DSH model picker. */
484
+ declare const WORKBUDDY_PROVIDER_DISPLAY_NAMES: Readonly<Record<WorkBuddyRegion, string>>;
402
485
  /** Provider idle ceiling while one stream read is outstanding. */
403
486
  declare const WORKBUDDY_STREAM_IDLE_TIMEOUT_MS = 300000;
404
487
  /** Constructor dependencies. */
@@ -406,6 +489,10 @@ interface WorkBuddyAdapterOptions {
406
489
  shim: WorkBuddyShim;
407
490
  store: WorkBuddyCredentialStore;
408
491
  catalog: WorkBuddyCatalog;
492
+ /** Provider route id this instance serves; defaults to the CN route. */
493
+ provider?: string;
494
+ /** pi-ai provider name and profile display name; defaults to the CN name. */
495
+ displayName?: string;
409
496
  /** Resolve the durable attachment service at request time, when present. */
410
497
  resolveAttachments?: () => AttachmentStore | undefined;
411
498
  }
@@ -529,6 +616,21 @@ declare const WORKBUDDY_MODELS_REFRESH_PATH = "/plugins/dsh-connect-workbuddy/mo
529
616
  declare const WORKBUDDY_ACCOUNTS_REFRESH_PATH = "/plugins/dsh-connect-workbuddy/accounts/refresh";
530
617
  /** Plugin-owned daily check-in action endpoint. */
531
618
  declare const WORKBUDDY_CHECKIN_PATH = "/plugins/dsh-connect-workbuddy/checkin";
619
+ /** Query parameter naming the region a card request addresses. */
620
+ declare const WORKBUDDY_REGION_PARAM = "region";
621
+ /** Every region, in card tab order. */
622
+ declare const WORKBUDDY_REGIONS: readonly WorkBuddyWebRegion[];
623
+ /**
624
+ * Address one region's status route. The two regions are separate provider
625
+ * stacks; every card request carries the region whose tab the user is on.
626
+ */
627
+ declare function withWorkBuddyRegion(path: string, region: WorkBuddyWebRegion): string;
628
+ /**
629
+ * Read the region parameter off a status-route URL. Absent means the domestic
630
+ * tab (`cn`); a present-but-unknown value returns undefined so the route can
631
+ * answer 400 instead of guessing.
632
+ */
633
+ declare function regionOfStatusUrl(url: string): WorkBuddyWebRegion | undefined;
532
634
  /** One credit package as the upstream returns it, node-free. */
533
635
  interface WorkBuddyWebCreditPackage {
534
636
  packageName: string;
@@ -638,30 +740,33 @@ type WorkBuddyWebUsage = {
638
740
  //#region src/web-status.d.ts
639
741
  /** Constructor dependencies. */
640
742
  interface WorkBuddyStatusRouteOptions {
641
- store: WorkBuddyCredentialStore;
743
+ /** The region-scoped credential store backing each region's requests. */
744
+ store(region: WorkBuddyRegion): WorkBuddyCredentialStore;
642
745
  client: Pick<WorkBuddyUpstreamClient, 'fetchCredits' | 'fetchCheckinStatus' | 'claimDailyCheckin'>;
643
746
  /**
644
- * The selected region's last-refreshed model directory (unfiltered) for card
645
- * display. Region-scoped because the CN and international apps expose
747
+ * The requested region's last-refreshed model directory (unfiltered) for
748
+ * card display. Region-scoped because the CN and international apps expose
646
749
  * different rosters; showing one region's directory on the other account is
647
750
  * the bug this parameter exists to prevent.
648
751
  */
649
752
  displayModels(region: WorkBuddyRegion): readonly WorkBuddyModelInfo[];
650
- /** The selected region's selection, stored as model ids. */
753
+ /** The requested region's selection, stored as model ids. */
651
754
  enabledModelIds(region: WorkBuddyRegion): readonly string[];
652
- /** Model ids the user opted into image input, for the selected region. */
755
+ /** Model ids the user opted into image input, for the requested region. */
653
756
  imageModelIds(region: WorkBuddyRegion): readonly string[];
654
- /** Saved local DSH context budgets by model id, for the selected region. */
757
+ /** Saved local DSH context budgets by model id, for the requested region. */
655
758
  contextBudgets(region: WorkBuddyRegion): Readonly<Record<string, number | undefined>>;
656
- /** Re-read the live catalog from the upstream. */
657
- discoverModels?(signal?: AbortSignal): Promise<readonly WorkBuddyModelInfo[]>;
759
+ /** Re-read the live catalog of one region from the upstream. */
760
+ discoverModels?(region: WorkBuddyRegion, signal?: AbortSignal): Promise<readonly WorkBuddyModelInfo[]>;
658
761
  }
659
762
  /**
660
- * Assemble the card's usage document. Sign-in state is read-only; credit is a
661
- * live billing answer whose failure degrades to `creditsError` rather than
662
- * failing the whole document.
763
+ * Assemble one region's card document. `region` is the tab the card is on;
764
+ * the region-scoped store already answers with only that region's accounts,
765
+ * so the document's model slots and account list are that region's by
766
+ * construction. Sign-in state is read-only; credit is a live billing answer
767
+ * whose failure degrades to `creditsError` rather than failing the document.
663
768
  */
664
- declare function workBuddyWebStatus(deps: WorkBuddyStatusRouteOptions): Promise<WorkBuddyWebUsage>;
769
+ declare function workBuddyWebStatus(deps: WorkBuddyStatusRouteOptions, region: WorkBuddyRegion): Promise<WorkBuddyWebUsage>;
665
770
  /**
666
771
  * Mount the read-only routes on a context where `webServer` is available.
667
772
  * The caller uses `ctx.inject(['webServer'], ...)`, so Desktop startup order
@@ -691,8 +796,18 @@ interface WorkBuddyRegionState {
691
796
  interface Config {
692
797
  /** Explicit WorkBuddy desktop auth-file path, overriding env and platform defaults. */
693
798
  authFile?: string;
694
- /** Stable local account selector; tokens remain outside settings. */
799
+ /**
800
+ * @deprecated Legacy single-slot account selector from before the dual
801
+ * provider split. It is attributed to whichever region the account actually
802
+ * belongs to (resolved once at startup from the local account scan); new
803
+ * writes go to {@link Config.accounts}.
804
+ */
695
805
  accountId?: string;
806
+ /**
807
+ * Per-region account selections, keyed `cn` | `global`. Each region's tab
808
+ * writes its own slot; tokens remain outside settings.
809
+ */
810
+ accounts?: Partial<Record<WorkBuddyRegion, string>>;
696
811
  /**
697
812
  * Per-region model state, keyed `cn` | `global`. The CN app and the
698
813
  * international WorkBuddy AI app expose different rosters, so each keeps its
@@ -725,11 +840,12 @@ declare const Config: z<Config>;
725
840
  */
726
841
  declare function regionStateOf(config: Config, region: WorkBuddyRegion): WorkBuddyRegionState;
727
842
  /**
728
- * Start the loopback endpoint, register the `workbuddy` provider, and
729
- * refresh the model catalog from the upstream once credentials allow it.
730
- * The static fallback catalog serves from the first moment, so an offline
731
- * upstream never leaves the provider empty.
843
+ * Start both regions' loopback endpoints, register the `workbuddy` (CN) and
844
+ * `workbuddy-global` (international) providers, and refresh each region's
845
+ * model catalog from the upstream once that region's credentials allow it.
846
+ * The static fallback catalogs serve from the first moment, so an offline
847
+ * upstream never leaves a provider empty.
732
848
  */
733
849
  declare function apply(ctx: Context, config: Config): void;
734
850
  //#endregion
735
- export { Config, FALLBACK_WORKBUDDY_MODELS, FALLBACK_WORKBUDDY_MODELS_GLOBAL, type UpstreamErrorKind, WORKBUDDY_ACCOUNTS_REFRESH_PATH, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_CHECKIN_PATH, WORKBUDDY_CONNECT_VERSION, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_MODELS_REFRESH_PATH, WORKBUDDY_PROVIDER, WORKBUDDY_SETTINGS_NS, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, WORKBUDDY_USAGE_PATH, type WorkBuddyAccountChoice, type WorkBuddyAdapter, type WorkBuddyAuthStatus, WorkBuddyCatalog, type WorkBuddyChatResult, type WorkBuddyCredential, WorkBuddyCredentialStore, type WorkBuddyCreditPackage, type WorkBuddyCredits, type WorkBuddyHostHeartbeat, type WorkBuddyModelInfo, type WorkBuddyReasoning, type WorkBuddyRefreshOutcome, WorkBuddyRegionState, type WorkBuddyShim, type WorkBuddyStatusRouteOptions, type WorkBuddyStoreOptions, WorkBuddyUpstreamClient, type WorkBuddyUpstreamModel, type WorkBuddyWebAccount, type WorkBuddyWebCheckin, type WorkBuddyWebCredits, type WorkBuddyWebModel, type WorkBuddyWebPackage, type WorkBuddyWebUsage, apply, authFileName, classifyUpstreamError, clearHostHeartbeat, createWorkBuddyAdapter, createWorkBuddyShim, defaultDesktopAuthCandidates, defaultDesktopAuthDirs, defaultDesktopAuthPath, deriveCatalog, fallbackModelsFor, inject, isHeartbeatProcessAlive, name, parseCreditMultiplier, parseReasoning, parseUpstreamModel, parseWorkBuddyAuth, prepareChatBody, processStartTimeMs, readHostHeartbeat, regionOf, regionStateOf, registerWorkBuddyStatusRoute, toPersistedWorkBuddyModel, workBuddyDisplayName, workBuddyModelInput, workBuddyThinkingLevelMap, workBuddyWebStatus, workbuddyAccountId, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath, writeHostHeartbeat };
851
+ export { Config, FALLBACK_WORKBUDDY_MODELS, FALLBACK_WORKBUDDY_MODELS_GLOBAL, type UpstreamErrorKind, WORKBUDDY_ACCOUNTS_REFRESH_PATH, WORKBUDDY_AUTH_FILENAME, WORKBUDDY_AUTH_FILE_ENV, WORKBUDDY_CHECKIN_PATH, WORKBUDDY_CONNECT_VERSION, WORKBUDDY_GLOBAL_PROVIDER, WORKBUDDY_HOST_HEARTBEAT_FILENAME, WORKBUDDY_MODELS_REFRESH_PATH, WORKBUDDY_PROVIDER, WORKBUDDY_PROVIDERS, WORKBUDDY_PROVIDER_DISPLAY_NAMES, WORKBUDDY_REGIONS, WORKBUDDY_REGION_PARAM, WORKBUDDY_SETTINGS_NS, WORKBUDDY_STREAM_IDLE_TIMEOUT_MS, WORKBUDDY_USAGE_PATH, type WorkBuddyAccountChoice, type WorkBuddyAdapter, type WorkBuddyAuthStatus, WorkBuddyCatalog, type WorkBuddyChatResult, type WorkBuddyCredential, WorkBuddyCredentialStore, type WorkBuddyCreditPackage, type WorkBuddyCredits, type WorkBuddyHostHeartbeat, type WorkBuddyModelInfo, type WorkBuddyReasoning, type WorkBuddyRefreshOutcome, WorkBuddyRegionState, type WorkBuddyShim, type WorkBuddyStatusRouteOptions, type WorkBuddyStoreOptions, WorkBuddyUpstreamClient, type WorkBuddyUpstreamModel, type WorkBuddyWebAccount, type WorkBuddyWebCheckin, type WorkBuddyWebCredits, type WorkBuddyWebModel, type WorkBuddyWebPackage, type WorkBuddyWebRegion, type WorkBuddyWebUsage, apply, authFileName, classifyUpstreamError, clearHostHeartbeat, createWorkBuddyAdapter, createWorkBuddyShim, defaultDesktopAuthCandidates, defaultDesktopAuthDirs, defaultDesktopAuthPath, deriveCatalog, fallbackModelsFor, inject, isHeartbeatProcessAlive, legacyWorkbuddyOwnAuthPath, name, parseCreditMultiplier, parseReasoning, parseUpstreamModel, parseWorkBuddyAuth, prepareChatBody, processStartTimeMs, readHostHeartbeat, regionOf, regionOfProvider, regionOfStatusUrl, regionStateOf, registerWorkBuddyStatusRoute, toPersistedWorkBuddyModel, withWorkBuddyRegion, workBuddyDisplayName, workBuddyModelInput, workBuddyThinkingLevelMap, workBuddyWebStatus, workbuddyAccountId, workbuddyHostHeartbeatPath, workbuddyOwnAuthPath, writeHostHeartbeat };