dsh-qoder-connect 0.1.4 → 0.1.8

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.
@@ -502,11 +502,18 @@ var QoderCatalog = class {
502
502
  useMaximumContextWindow = false;
503
503
  /** Per-model window overrides (model id → tokens); an override wins over the preference. */
504
504
  modelContextWindows = {};
505
+ /** Disabled model IDs (blacklist); disabled models are filtered out from current(). */
506
+ disabledModels = /* @__PURE__ */ new Set();
505
507
  constructor(initial = FALLBACK_QODER_MODELS) {
506
508
  this.models = initial;
507
509
  }
508
- /** Current entries; empty while the variant has no usable credential. */
510
+ /** Current entries; empty while the variant has no usable credential, excluding disabled models. */
509
511
  current() {
512
+ if (!this.visible) return [];
513
+ return this.all().filter((model) => !this.disabledModels.has(model.id));
514
+ }
515
+ /** All configured entries including disabled ones; empty while the variant has no usable credential. */
516
+ all() {
510
517
  if (!this.visible) return [];
511
518
  return this.models.map((model) => {
512
519
  const override = this.modelContextWindows[model.id];
@@ -557,6 +564,20 @@ var QoderCatalog = class {
557
564
  return true;
558
565
  }
559
566
  /**
567
+ * Replace the disabled model IDs wholesale. Returns whether the set
568
+ * changed, so the caller can skip an invalidation over an identical write.
569
+ */
570
+ setDisabledModels(disabledModels) {
571
+ const next = new Set(disabledModels);
572
+ if (next.size === this.disabledModels.size && [...next].every((id) => this.disabledModels.has(id))) return false;
573
+ this.disabledModels = next;
574
+ return true;
575
+ }
576
+ /** Current disabled model IDs. */
577
+ getDisabledModels() {
578
+ return Array.from(this.disabledModels);
579
+ }
580
+ /**
560
581
  * Models to fall back to when the upstream fetch fails; ignores
561
582
  * visibility, because the caller asking for the fallback already knows the
562
583
  * credential state.
@@ -876,6 +897,14 @@ function getQoderUserStatusUrl(region = "global") {
876
897
  const { openApiUrl } = resolveQoderEndpoints(region);
877
898
  return `${openApiUrl}/api/v3/user/status`;
878
899
  }
900
+ function getQoderCampaignsUrl(region = "global") {
901
+ const { openApiUrl } = resolveQoderEndpoints(region);
902
+ return `${openApiUrl}/sash/api/v1/me/campaigns`;
903
+ }
904
+ function getQoderClaimCampaignUrl(region = "global", campaignId) {
905
+ const { openApiUrl } = resolveQoderEndpoints(region);
906
+ return `${openApiUrl}/sash/api/v1/me/campaigns/${encodeURIComponent(campaignId)}/claim`;
907
+ }
879
908
  //#endregion
880
909
  //#region src/qoder/errors.ts
881
910
  /**
@@ -2359,6 +2388,7 @@ var QoderUsageReader = class {
2359
2388
  return {
2360
2389
  userQuota: normalizeQuota(data.userQuota),
2361
2390
  orgResourcePackage: normalizeQuota(data.orgResourcePackage),
2391
+ addOnQuota: normalizeQuota(data.addOnQuota),
2362
2392
  totalUsagePercentage: typeof data.totalUsagePercentage === "number" ? data.totalUsagePercentage : void 0,
2363
2393
  isQuotaExceeded: typeof data.isQuotaExceeded === "boolean" ? data.isQuotaExceeded : false,
2364
2394
  expiresAt: normalizeExpiresAt(data.expiresAt),
@@ -2408,6 +2438,135 @@ var QoderUsageReader = class {
2408
2438
  }
2409
2439
  };
2410
2440
  //#endregion
2441
+ //#region src/qoder/transport/checkin.ts
2442
+ const defaultCheckInTimeoutMs = 15e3;
2443
+ var QoderCheckInService = class {
2444
+ authService;
2445
+ fetchImpl;
2446
+ region;
2447
+ variantId;
2448
+ logger;
2449
+ timeoutMs;
2450
+ constructor(options) {
2451
+ this.authService = options.authService;
2452
+ this.fetchImpl = options.fetch ?? globalThis.fetch;
2453
+ this.region = options.region ?? "china";
2454
+ this.variantId = options.variantId ?? "qoder";
2455
+ this.logger = options.logger;
2456
+ this.timeoutMs = options.timeoutMs ?? defaultCheckInTimeoutMs;
2457
+ }
2458
+ async fetchCampaigns(token, signal) {
2459
+ const url = getQoderCampaignsUrl(this.region);
2460
+ const data = await openApiJsonRequest(this.fetchImpl, {
2461
+ url,
2462
+ token,
2463
+ headers: { "cosy-clienttype": "10" },
2464
+ signal,
2465
+ timeoutMs: this.timeoutMs,
2466
+ logger: this.logger,
2467
+ operation: "Campaigns",
2468
+ logCategory: "campaigns.list"
2469
+ });
2470
+ return Array.isArray(data?.campaigns) ? data.campaigns : [];
2471
+ }
2472
+ async claimCampaign(token, campaignId, signal) {
2473
+ const url = getQoderClaimCampaignUrl(this.region, campaignId);
2474
+ const { openApiUrl } = resolveQoderEndpoints(this.region);
2475
+ return openApiJsonRequest(this.fetchImpl, {
2476
+ url,
2477
+ method: "POST",
2478
+ token,
2479
+ headers: {
2480
+ origin: openApiUrl,
2481
+ "cosy-clienttype": "10"
2482
+ },
2483
+ signal,
2484
+ timeoutMs: this.timeoutMs,
2485
+ logger: this.logger,
2486
+ operation: "ClaimCampaign",
2487
+ logCategory: "campaigns.claim"
2488
+ });
2489
+ }
2490
+ getTodayDateString() {
2491
+ const now = /* @__PURE__ */ new Date();
2492
+ const utc8Time = new Date(now.getTime() + (now.getTimezoneOffset() + 480) * 6e4);
2493
+ return `${utc8Time.getFullYear()}-${String(utc8Time.getMonth() + 1).padStart(2, "0")}-${String(utc8Time.getDate()).padStart(2, "0")}`;
2494
+ }
2495
+ async checkIn(pat, signal) {
2496
+ const today = this.getTodayDateString();
2497
+ const nowMs = Date.now();
2498
+ if (!pat || typeof pat !== "string" || pat.trim() === "") return {
2499
+ variantId: this.variantId,
2500
+ date: today,
2501
+ timestamp: nowMs,
2502
+ status: "error",
2503
+ message: "No PAT available"
2504
+ };
2505
+ const executeWithToken = async (authToken) => {
2506
+ const benefitCampaign = (await this.fetchCampaigns(authToken, signal)).find((c) => c.actionType === "CLAIM_BENEFIT");
2507
+ if (!benefitCampaign) return {
2508
+ variantId: this.variantId,
2509
+ date: today,
2510
+ timestamp: nowMs,
2511
+ status: "no-campaign",
2512
+ message: "No claimable benefit campaign found for this region"
2513
+ };
2514
+ if (benefitCampaign.claimStatus === "CLAIMED") return {
2515
+ variantId: this.variantId,
2516
+ date: today,
2517
+ timestamp: nowMs,
2518
+ status: "already-claimed",
2519
+ campaignKey: benefitCampaign.campaignKey,
2520
+ amount: benefitCampaign.benefit?.amount ?? 100,
2521
+ message: "Already claimed today"
2522
+ };
2523
+ const claimResult = await this.claimCampaign(authToken, benefitCampaign.campaignId, signal);
2524
+ const isClaimed = claimResult.status === "CLAIMED";
2525
+ const replayed = Boolean(claimResult.replayed);
2526
+ const amount = claimResult.benefit?.amount ?? benefitCampaign.benefit?.amount ?? 100;
2527
+ if (isClaimed) return {
2528
+ variantId: this.variantId,
2529
+ date: today,
2530
+ timestamp: nowMs,
2531
+ status: replayed ? "already-claimed" : "claimed",
2532
+ amount,
2533
+ campaignKey: benefitCampaign.campaignKey,
2534
+ message: replayed ? "Already claimed today" : `Successfully claimed ${amount} credits`
2535
+ };
2536
+ return {
2537
+ variantId: this.variantId,
2538
+ date: today,
2539
+ timestamp: nowMs,
2540
+ status: "error",
2541
+ campaignKey: benefitCampaign.campaignKey,
2542
+ message: `Claim returned status ${claimResult.status ?? "unknown"}`
2543
+ };
2544
+ };
2545
+ try {
2546
+ const creds = await this.authService.getCredentials(pat, signal);
2547
+ try {
2548
+ return await executeWithToken(creds.authToken);
2549
+ } catch (innerError) {
2550
+ if (isQoderAuthRejection(innerError)) {
2551
+ this.logger?.warn?.(`[Qoder CheckIn] Auth token rejected, retrying with fresh exchange`);
2552
+ return await executeWithToken((await this.authService.exchangeFresh(pat, signal)).authToken);
2553
+ }
2554
+ throw innerError;
2555
+ }
2556
+ } catch (error) {
2557
+ const message = error instanceof Error ? error.message : String(error);
2558
+ this.logger?.error?.(`[Qoder CheckIn] Check-in failed: ${message}`);
2559
+ return {
2560
+ variantId: this.variantId,
2561
+ date: today,
2562
+ timestamp: nowMs,
2563
+ status: "error",
2564
+ message
2565
+ };
2566
+ }
2567
+ }
2568
+ };
2569
+ //#endregion
2411
2570
  //#region src/qoder/transport/wire/encoding.ts
2412
2571
  /**
2413
2572
  * Qoder WAF Body encoding algorithm.
@@ -3058,6 +3217,7 @@ var DefaultQoderTransport = class {
3058
3217
  metadataTimeoutMs;
3059
3218
  auth;
3060
3219
  usage;
3220
+ checkInService;
3061
3221
  modelFlights = new SingleFlight();
3062
3222
  attachments;
3063
3223
  imageUploader;
@@ -3119,6 +3279,13 @@ var DefaultQoderTransport = class {
3119
3279
  region: this.region,
3120
3280
  timeoutMs: this.metadataTimeoutMs
3121
3281
  });
3282
+ this.checkInService = new QoderCheckInService({
3283
+ authService: this.auth,
3284
+ fetch: this.fetchImpl,
3285
+ logger: this.logger,
3286
+ region: this.region,
3287
+ timeoutMs: this.metadataTimeoutMs
3288
+ });
3122
3289
  }
3123
3290
  stream(options, model) {
3124
3291
  return this.generate(options, model);
@@ -3184,6 +3351,10 @@ var DefaultQoderTransport = class {
3184
3351
  signal: options?.signal
3185
3352
  });
3186
3353
  }
3354
+ async checkIn(signal) {
3355
+ const pat = await this.requirePat(signal);
3356
+ return this.checkInService.checkIn(pat, signal);
3357
+ }
3187
3358
  async requirePat(signal) {
3188
3359
  if (signal?.aborted) throw aborted("Qoder request was aborted.");
3189
3360
  const pat = (await this.resolvePat()).trim();
@@ -3838,6 +4009,13 @@ var QoderUpstreamClient = class {
3838
4009
  size: org.total,
3839
4010
  ...usage.expiresAt === void 0 ? {} : { packageEndTime: usage.expiresAt }
3840
4011
  });
4012
+ const addOn = usage.addOnQuota;
4013
+ if (addOn !== void 0) accounts.push({
4014
+ packageName: "赠送额度",
4015
+ remain: addOn.remaining,
4016
+ size: addOn.total,
4017
+ ...usage.expiresAt === void 0 ? {} : { packageEndTime: usage.expiresAt }
4018
+ });
3841
4019
  const unlimited = usage.isQuotaExceeded === false && personal !== void 0 && personal.total === 0;
3842
4020
  return {
3843
4021
  total: usage.totalUsagePercentage ?? personal?.percentage ?? 0,
@@ -3848,6 +4026,12 @@ var QoderUpstreamClient = class {
3848
4026
  };
3849
4027
  }
3850
4028
  /**
4029
+ * Run daily benefit check-in for this client's variant.
4030
+ */
4031
+ async checkIn(signal) {
4032
+ return this.transport.checkIn(signal);
4033
+ }
4034
+ /**
3851
4035
  * Stream one translated chat-completions request.
3852
4036
  *
3853
4037
  * The first transport chunk is pulled before the response is constructed:
@@ -4082,7 +4266,7 @@ const DEFAULT_MAX_TOKENS = 32768;
4082
4266
  const DEFAULT_CONTEXT_WINDOW = 18e4;
4083
4267
  //#endregion
4084
4268
  //#region src/version.ts
4085
- const QODER_CONNECT_VERSION = "0.1.4";
4269
+ const QODER_CONNECT_VERSION = "0.1.8";
4086
4270
  //#endregion
4087
4271
  //#region src/host-heartbeat.ts
4088
4272
  /**
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "dsh-qoder-connect",
3
3
  "displayName": "DSH Qoder Connect",
4
4
  "description": "将 Qoder(国内版 / 国际版)的模型接入 DeepSeek Harness — bring Qoder (CN & international) models into DeepSeek Harness.",
5
- "version": "0.1.4",
5
+ "version": "0.1.8",
6
6
  "author": "corrinehu",
7
7
  "keywords": [
8
8
  "dsh-plugin",