dsh-connect-minimaxcode 0.4.2 → 0.5.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/client.js CHANGED
@@ -18,6 +18,7 @@
18
18
  const BUNDLE_NAME = 'dsh-connect-minimaxcode';
19
19
  const NAMESPACE = 'dsh-connect-minimaxcode';
20
20
  const STATUS_PATH = '/plugins/dsh-connect-minimaxcode/status';
21
+ const ACTION_PATH = '/plugins/dsh-connect-minimaxcode/action';
21
22
  const POLL_INTERVAL_MS = 20000;
22
23
  const zh = {
23
24
  title: 'MiniMax Code 积分',
@@ -36,9 +37,28 @@
36
37
  expiringIn: '{amount} 积分 · {date} 到期',
37
38
  expiringNone: '7 天内没有积分到期',
38
39
  expiringSoon: '30 天内到期 {amount} 积分',
39
- nextExpiry: '最近一笔到期',
40
+ nextExpiry: '最近到期日',
40
41
  lots: '到期批次',
41
42
  noLots: '没有待到期的积分批次',
43
+ loginTitle: '登录',
44
+ loginPlugin: '已在插件内登录',
45
+ loginDesktop: '使用 MiniMax Code 桌面 App 的登录态',
46
+ loginSignedOut: '未登录',
47
+ loginPending: '等待你在浏览器里确认',
48
+ loginCode: '设备码',
49
+ loginOpen: '打开授权页面',
50
+ loginPolled: '我已授权,检查一下',
51
+ loginStart: '登录 MiniMax Code',
52
+ signinTitle: '每日签到',
53
+ signinToday: '第 {day} 天 · {points} 积分',
54
+ signinClaimed: '今日已签到',
55
+ signinClaimable: '今日可签到',
56
+ signinUnavailable: '签到信息不可用',
57
+ signinCycle: '一轮共 {points} 积分',
58
+ signinLast: '上次签到 {day},{points} 积分',
59
+ signinAuto: '每天自动签到',
60
+ signinClaim: '签到',
61
+ claimableToday: '今日可签到',
42
62
  };
43
63
  const en = {
44
64
  title: 'MiniMax Code credits',
@@ -60,6 +80,25 @@
60
80
  nextExpiry: 'Next expiry',
61
81
  lots: 'Expiring grants',
62
82
  noLots: 'No grant is waiting to expire',
83
+ loginTitle: 'Sign-in',
84
+ loginPlugin: 'Signed in through the plugin',
85
+ loginDesktop: 'Using the MiniMax Code desktop app sign-in',
86
+ loginSignedOut: 'Not signed in',
87
+ loginPending: 'Waiting for you to confirm in the browser',
88
+ loginCode: 'Device code',
89
+ loginOpen: 'Open the authorization page',
90
+ loginPolled: 'I authorized it — check',
91
+ loginStart: 'Sign in to MiniMax Code',
92
+ signinTitle: 'Daily check-in',
93
+ signinToday: 'Day {day} · {points} credits',
94
+ signinClaimed: 'Claimed today',
95
+ signinClaimable: 'Claimable today',
96
+ signinUnavailable: 'Check-in information unavailable',
97
+ signinCycle: '{points} credits per cycle',
98
+ signinLast: 'Last claim on {day}, {points} credits',
99
+ signinAuto: 'Claim daily on its own',
100
+ signinClaim: 'Claim',
101
+ claimableToday: 'Claimable today',
63
102
  };
64
103
  const loader = globalThis.__ModuleLoader__;
65
104
  if (loader === undefined)
@@ -144,7 +183,7 @@
144
183
  function useStatus() {
145
184
  const [status, setStatus] = React.useState(undefined);
146
185
  React.useEffect(() => subscribe(setStatus), []);
147
- return status;
186
+ return [status, () => { void load(); }];
148
187
  }
149
188
  /** A row of label and value. */
150
189
  function row(label, value) {
@@ -164,7 +203,7 @@
164
203
  }
165
204
  /** The pill in the composer's dock. */
166
205
  function CreditPill(props) {
167
- const status = useStatus();
206
+ const [status] = useStatus();
168
207
  const [open, setOpen] = React.useState(false);
169
208
  const t = props.t ?? ((key, params) => {
170
209
  const template = zh[key] ?? key;
@@ -192,6 +231,86 @@
192
231
  onClick: () => setOpen(!open),
193
232
  }, label), open ? detail : null);
194
233
  }
234
+ /** Ask the host to do something, and let the card read the result. */
235
+ async function act(action, body) {
236
+ try {
237
+ await fetch(`${ACTION_PATH}?action=${encodeURIComponent(action)}`, {
238
+ method: 'POST',
239
+ headers: { 'content-type': 'application/json' },
240
+ body: JSON.stringify(body ?? {}),
241
+ });
242
+ }
243
+ catch {
244
+ // The card shows the state the host reports; a failed request is
245
+ // reported there too.
246
+ }
247
+ // Read the state the host produced, so the card shows what happened.
248
+ void load();
249
+ }
250
+ /**
251
+ * The sign-in block.
252
+ *
253
+ * The plugin can sign in on its own: the person authorizes in their own
254
+ * browser, and the plugin never sees a password. When the desktop app
255
+ * is already signed in, that credential is used and this block says so.
256
+ */
257
+ function loginBlock(t, login, refresh) {
258
+ if (login === undefined)
259
+ return null;
260
+ if (login.state === 'signed-in') {
261
+ return h('div', { style: sectionBoxStyle, 'data-minimax-login': true }, row(t('loginTitle'), login.source === 'plugin' ? t('loginPlugin') : t('loginDesktop')));
262
+ }
263
+ if (login.state === 'pending') {
264
+ return h('div', { style: sectionBoxStyle, 'data-minimax-login': true }, row(t('loginTitle'), t('loginPending')), login.userCode !== undefined && login.userCode.length > 0
265
+ ? row(t('loginCode'), login.userCode)
266
+ : null, login.verificationUriComplete !== undefined && login.verificationUriComplete.length > 0
267
+ ? h('a', {
268
+ href: login.verificationUriComplete,
269
+ target: '_blank',
270
+ rel: 'noreferrer',
271
+ style: linkStyle,
272
+ }, t('loginOpen'))
273
+ : null, h('button', { type: 'button', style: actionButtonStyle, onClick: () => { void act('login-poll'); } }, t('loginPolled')));
274
+ }
275
+ return h('div', { style: sectionBoxStyle, 'data-minimax-login': true }, row(t('loginTitle'), t('loginSignedOut')), h('button', { type: 'button', style: actionButtonStyle, onClick: () => { void act('login-start'); } }, t('loginStart')));
276
+ }
277
+ /**
278
+ * The check-in block.
279
+ *
280
+ * The cycle is seven days; the card shows which day it is and what the
281
+ * day is worth, and lets the person claim it — or leave it to the
282
+ * plugin, once a day, and only when the server says the day can be
283
+ * claimed.
284
+ */
285
+ function signinBlock(t, signin, refresh) {
286
+ if (signin === undefined)
287
+ return null;
288
+ return h('div', { style: sectionBoxStyle, 'data-minimax-signin': true }, h('div', { style: sectionTitleStyle }, t('signinTitle')), signin.today === undefined
289
+ ? h('div', { style: mutedStyle }, t('signinUnavailable'))
290
+ : h('div', { style: rowStyle }, h('span', null, t('signinToday', { day: signin.today.dayNo, points: signin.today.points })), h('span', null, signin.today.claimed ? t('signinClaimed') : signin.today.claimable ? t('signinClaimable') : t('signinUnavailable'))), (signin.days ?? []).length === 0
291
+ ? null
292
+ : h('div', { style: dayRowStyle }, ...(signin.days ?? []).map(day_ => h('span', {
293
+ key: `day-${day_.dayNo}`,
294
+ title: `${day_.dayNo}: ${day_.points}`,
295
+ style: {
296
+ ...dayDotStyle,
297
+ ...day_.isToday ? dayTodayStyle : {},
298
+ ...day_.status === 3 ? dayClaimedStyle : {},
299
+ },
300
+ }, String(day_.dayNo)))), signin.cyclePoints !== undefined
301
+ ? h('div', { style: mutedStyle }, t('signinCycle', { points: signin.cyclePoints }))
302
+ : null, signin.lastClaimDay !== undefined
303
+ ? h('div', { style: mutedStyle }, t('signinLast', { day: signin.lastClaimDay, points: signin.lastClaimPoints ?? 0 }))
304
+ : null, h('div', { style: rowStyle }, h('label', { style: checkboxStyle }, h('input', {
305
+ type: 'checkbox',
306
+ checked: signin.autoCheckin === true,
307
+ onChange: (event) => {
308
+ void act('settings', { autoCheckin: event.target?.checked === true });
309
+ },
310
+ }), h('span', null, t('signinAuto'))), signin.today?.claimable === true
311
+ ? h('button', { type: 'button', style: actionButtonStyle, onClick: () => { void act('claim'); } }, t('signinClaim'))
312
+ : null), signin.error !== undefined ? h('div', { style: mutedStyle }, signin.error) : null);
313
+ }
195
314
  /**
196
315
  * The card on the plugin's own page.
197
316
  *
@@ -199,29 +318,31 @@
199
318
  * is the full card, where the expiry detail belongs.
200
319
  */
201
320
  function PluginPage(props) {
202
- const status = useStatus();
321
+ const [status, refresh] = useStatus();
203
322
  const t = props.t ?? ((key, params) => {
204
323
  const template = zh[key] ?? key;
205
324
  return template.replace(/\{(\w+)\}/g, (_match, name) => String(params?.[name] ?? ''));
206
325
  });
207
326
  const credits_ = status?.credits;
208
327
  const usage = status?.usage;
328
+ const signin = status?.signin;
329
+ const login = status?.login;
209
330
  const available = credits_?.state === 'ok' && credits_.total !== undefined;
210
331
  if (props.view === 'summary') {
211
332
  if (!available)
212
333
  return h('span', null, t('unavailable'));
213
334
  return h('span', { 'data-minimax-credits-summary': true }, t('balance', { total: credits(credits_.total, 2) }), credits_.expiring !== undefined && credits_.expiring > 0
214
335
  ? ` · ${t('expiringIn', { amount: credits(credits_.expiring, 2), date: day(credits_.nextExpiryAtMs) })}`
215
- : '');
336
+ : '', signin?.today?.claimable === true ? ` · ${t('claimableToday')}` : '');
216
337
  }
217
338
  if (!available) {
218
- return h('div', { style: cardStyle, 'data-minimax-credits-card': true }, h('div', { style: cardTitleStyle }, t('title')), h('div', { style: rowStyle }, h('span', null, t('unavailable')), h('span', null, credits_?.state === 'expired' ? t('expired') : '')));
339
+ return h('div', { style: cardStyle, 'data-minimax-credits-card': true }, h('div', { style: cardTitleStyle }, t('title')), h('div', { style: rowStyle }, h('span', null, t('unavailable')), h('span', null, credits_?.state === 'expired' ? t('expired') : '')), loginBlock(t, login, refresh));
219
340
  }
220
- return h('div', { style: cardStyle, 'data-minimax-credits-card': true }, h('div', { style: cardTitleStyle }, t('title')), h('div', { style: bigNumberStyle }, t('balance', { total: credits(credits_.total, 2) })), row(t('free'), credits(credits_.free, 3)), row(t('purchased'), credits(credits_.purchased, 3)), expiryBlock(t, credits_), h('div', { style: sectionTitleStyle }, t('lots')), (credits_.upcoming ?? []).length === 0
341
+ return h('div', { style: cardStyle, 'data-minimax-credits-card': true }, h('div', { style: cardTitleStyle }, t('title')), h('div', { style: bigNumberStyle }, t('balance', { total: credits(credits_.total, 2) })), row(t('free'), credits(credits_.free, 3)), row(t('purchased'), credits(credits_.purchased, 3)), expiryBlock(t, credits_), signinBlock(t, signin, refresh), h('div', { style: sectionTitleStyle }, t('lots')), (credits_.upcoming ?? []).length === 0
221
342
  ? h('div', { style: mutedStyle }, t('noLots'))
222
343
  : h('ul', { style: listStyle }, ...(credits_.upcoming ?? []).map((lot, index) => h('li', { key: `${lot.expireAtMs}-${index}`, style: rowStyle }, h('span', null, day(lot.expireAtMs)), h('span', null, credits(lot.amount, 2))))), usage !== undefined
223
344
  ? h('div', { style: usageBoxStyle }, row(t('tokens', { count: tokens(usage.inputTokens + usage.outputTokens + usage.cacheReadTokens) }), ''), row(t('cost', { amount: money(usage.estimatedCny) }), ''), usage.requests > 0 ? row(t('requests', { count: usage.requests }), usage.lastModel ?? '') : null)
224
- : null, credits_.updatedAtMs !== undefined
345
+ : null, loginBlock(t, login, refresh), credits_.updatedAtMs !== undefined
225
346
  ? h('div', { style: mutedStyle }, t('updated', { time: new Date(credits_.updatedAtMs).toLocaleTimeString() }))
226
347
  : null);
227
348
  }
@@ -283,6 +404,42 @@
283
404
  const sectionTitleStyle = { marginTop: '8px', fontWeight: 600 };
284
405
  const mutedStyle = { opacity: 0.7, fontSize: '12px' };
285
406
  const listStyle = { listStyle: 'none', margin: 0, padding: 0 };
407
+ const sectionBoxStyle = {
408
+ marginTop: '10px',
409
+ paddingTop: '8px',
410
+ borderTop: '1px solid var(--dsh-panel-border, currentColor)',
411
+ display: 'flex',
412
+ flexDirection: 'column',
413
+ gap: '6px',
414
+ };
415
+ const actionButtonStyle = {
416
+ appearance: 'none',
417
+ background: 'transparent',
418
+ border: '1px solid var(--dsh-panel-border, currentColor)',
419
+ borderRadius: '6px',
420
+ color: 'inherit',
421
+ cursor: 'pointer',
422
+ font: 'inherit',
423
+ fontSize: '12px',
424
+ padding: '2px 10px',
425
+ width: 'fit-content',
426
+ };
427
+ const linkStyle = { color: 'inherit', fontSize: '12px' };
428
+ const checkboxStyle = { display: 'inline-flex', alignItems: 'center', gap: '6px', fontSize: '12px' };
429
+ const dayRowStyle = { display: 'flex', gap: '6px', flexWrap: 'wrap' };
430
+ const dayDotStyle = {
431
+ display: 'inline-flex',
432
+ alignItems: 'center',
433
+ justifyContent: 'center',
434
+ width: '22px',
435
+ height: '22px',
436
+ borderRadius: '999px',
437
+ border: '1px solid var(--dsh-panel-border, currentColor)',
438
+ fontSize: '11px',
439
+ opacity: 0.6,
440
+ };
441
+ const dayTodayStyle = { opacity: 1, fontWeight: 600 };
442
+ const dayClaimedStyle = { background: 'var(--dsh-panel-border, currentColor)', opacity: 0.9 };
286
443
  const usageBoxStyle = {
287
444
  marginTop: '8px',
288
445
  paddingTop: '6px',
package/lib/index.d.ts CHANGED
@@ -475,9 +475,304 @@ declare class UsageMeter {
475
475
  reset(): void;
476
476
  }
477
477
  //#endregion
478
+ //#region src/credentials.d.ts
479
+ /** The file the plugin's own credential lives in. */
480
+ declare const PLUGIN_CREDENTIAL_FILENAME = ".minimaxcode-auth.json";
481
+ /** The file the plugin's own settings and session live in. */
482
+ declare const PLUGIN_STATE_FILENAME = ".minimaxcode-state.json";
483
+ /** The credential the plugin obtained for itself. */
484
+ interface PluginCredential {
485
+ /** The document's shape. */
486
+ schemaVersion: number;
487
+ /** The OAuth client the token was issued to. */
488
+ clientId: string;
489
+ /** The scopes the token carries. */
490
+ scopes: string[];
491
+ /** The audience the token was issued for. */
492
+ audience: string;
493
+ /** The `mmoat_` bearer token. */
494
+ accessToken: string;
495
+ /** The refresh token, when the server issued one. */
496
+ refreshToken?: string;
497
+ /** Expiry in milliseconds since the epoch. */
498
+ expiresAtMs?: number;
499
+ /** When the token was obtained, in milliseconds since the epoch. */
500
+ obtainedAtMs?: number;
501
+ }
502
+ /** A device login waiting for the person to authorize it. */
503
+ interface DeviceLoginSession {
504
+ /** The device code the token endpoint is polled with. */
505
+ deviceCode: string;
506
+ /** The PKCE verifier that goes with the device code. */
507
+ codeVerifier: string;
508
+ /** The code the person types on the account page. */
509
+ userCode: string;
510
+ /** Where to authorize. */
511
+ verificationUri: string;
512
+ /** Where to authorize, with the code filled in. */
513
+ verificationUriComplete: string;
514
+ /** When the device code stops working, in milliseconds since the epoch. */
515
+ expiresAtMs: number;
516
+ }
517
+ /** The plugin's own settings. */
518
+ interface PluginState {
519
+ /** The document's shape. */
520
+ schemaVersion: number;
521
+ /** Whether the plugin claims the daily grant on its own. */
522
+ autoCheckin: boolean;
523
+ /** The day the last claim happened, in the account's time zone. */
524
+ lastClaimDay?: string;
525
+ /** When the last claim happened, in milliseconds since the epoch. */
526
+ lastClaimAtMs?: number;
527
+ /** The points the last claim granted. */
528
+ lastClaimPoints?: number;
529
+ /** A device login waiting to be authorized. */
530
+ deviceLogin?: DeviceLoginSession;
531
+ }
532
+ /**
533
+ * The Harness home.
534
+ *
535
+ * The host resolves it the same way: the `DSH_HOME` environment variable when
536
+ * it is set, otherwise `~/.dsh`. The plugin resolves it on its own rather
537
+ * than importing the host's helper, so it also works in a plain command line
538
+ * where the host's packages are not installed.
539
+ */
540
+ declare function dshHome(env?: NodeJS.ProcessEnv): string;
541
+ /** Where the plugin's credential lives. */
542
+ declare function pluginCredentialPath(env?: NodeJS.ProcessEnv): string;
543
+ /** Where the plugin's state lives. */
544
+ declare function pluginStatePath(env?: NodeJS.ProcessEnv): string;
545
+ /** Read the plugin's own credential. */
546
+ declare function readPluginCredential(env?: NodeJS.ProcessEnv): Promise<PluginCredential | undefined>;
547
+ /** Write the plugin's own credential. */
548
+ declare function writePluginCredential(credential: PluginCredential, env?: NodeJS.ProcessEnv): Promise<void>;
549
+ /** Read the plugin's own state. */
550
+ declare function readPluginState(env?: NodeJS.ProcessEnv): Promise<PluginState>;
551
+ /** Write the plugin's own state. */
552
+ declare function writePluginState(state: PluginState, env?: NodeJS.ProcessEnv): Promise<void>;
553
+ //#endregion
554
+ //#region src/login.d.ts
555
+ /** The endpoint that issues device codes. */
556
+ declare const DEVICE_CODE_PATH = "/oauth2/device/code";
557
+ /** The device authorization grant type. */
558
+ declare const DEVICE_CODE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
559
+ /** How a device login is going. */
560
+ type DeviceLoginState =
561
+ /** The person has not authorized it yet. */
562
+ {
563
+ state: 'pending';
564
+ } |
565
+ /** The authorization server asked the client to slow down. */
566
+ {
567
+ state: 'slow-down';
568
+ } |
569
+ /** The device code expired before the person authorized it. */
570
+ {
571
+ state: 'expired';
572
+ message?: string;
573
+ } |
574
+ /** The person declined. */
575
+ {
576
+ state: 'denied';
577
+ message?: string;
578
+ } |
579
+ /** The grant arrived. */
580
+ {
581
+ state: 'signed-in';
582
+ credential: PluginCredential;
583
+ };
584
+ /**
585
+ * Make a PKCE verifier and its S256 challenge.
586
+ *
587
+ * The verifier is 43 characters of base64url entropy, which is what RFC 7636
588
+ * asks for, and it never leaves this process.
589
+ */
590
+ declare function makePkce(): {
591
+ verifier: string;
592
+ challenge: string;
593
+ };
594
+ /**
595
+ * Ask for a device code.
596
+ *
597
+ * @returns The session the person authorizes, or `undefined` when the server
598
+ * would not issue one.
599
+ */
600
+ declare function startDeviceLogin(region: MinimaxRegion, fetchImpl?: typeof fetch, now?: number): Promise<DeviceLoginSession | undefined>;
601
+ /**
602
+ * Poll for the grant.
603
+ *
604
+ * The authorization server answers `400` while the person has not authorized
605
+ * yet, with `authorization_pending` in the body; that is a normal answer, not
606
+ * an error.
607
+ */
608
+ declare function pollDeviceLogin(session: DeviceLoginSession, region: MinimaxRegion, fetchImpl?: typeof fetch, now?: number): Promise<DeviceLoginState>;
609
+ /**
610
+ * Exchange the stored refresh token for a fresh access token.
611
+ *
612
+ * The authorization server rotates the refresh token, so the new pair
613
+ * replaces the old one wherever it is kept.
614
+ */
615
+ declare function refreshPluginCredential(credential: PluginCredential, region: MinimaxRegion, fetchImpl?: typeof fetch, now?: number): Promise<PluginCredential>;
616
+ //#endregion
617
+ //#region src/signin.d.ts
618
+ /** The claim endpoint. */
619
+ declare const SIGNIN_CLAIM_PATH = "/minimax-cloud/api/v1/signin/claim";
620
+ /** The cycle endpoint, which reports the panel. */
621
+ declare const SIGNIN_PANEL_PATH = "/minimax-cloud/api/v1/signin/status";
622
+ /** Per-day status, as the desktop app enumerates it. */
623
+ declare const SIGNIN_DAY_STATUS: {
624
+ /** The day has not come yet. */
625
+ readonly upcoming: 1;
626
+ /** The day is today and can be claimed. */
627
+ readonly claimable: 2;
628
+ /** The day is claimed. */
629
+ readonly claimed: 3;
630
+ /** The day does not grant anything. */
631
+ readonly disabled: 4;
632
+ };
633
+ /** The status of one day in the cycle. */
634
+ type SigninDayStatus = (typeof SIGNIN_DAY_STATUS)[keyof typeof SIGNIN_DAY_STATUS];
635
+ /** How many days a cycle runs. */
636
+ declare const SIGNIN_CYCLE_DAYS = 7;
637
+ /** The outcome of a claim. */
638
+ declare const SIGNIN_CLAIM_RESULT: {
639
+ /** The grant was credited. */
640
+ readonly credited: 1;
641
+ /** Today was already claimed; nothing was granted. */
642
+ readonly alreadyClaimed: 2;
643
+ };
644
+ /** The outcome of a claim. */
645
+ type SigninClaimResult = (typeof SIGNIN_CLAIM_RESULT)[keyof typeof SIGNIN_CLAIM_RESULT];
646
+ /** One day of the cycle. */
647
+ interface SigninDay {
648
+ /** Which day of the cycle this is, 1 to 7. */
649
+ dayNo: number;
650
+ /** Points the day grants. */
651
+ points: number;
652
+ /** Extra points on top, when the day carries a bonus. */
653
+ bonusPoints: number;
654
+ /** The day's status. */
655
+ status: SigninDayStatus;
656
+ /** Whether this is today. */
657
+ isToday: boolean;
658
+ }
659
+ /** The cycle as the server reports it. */
660
+ interface SigninPanel {
661
+ /** Which cycle the account is in. */
662
+ scene: number;
663
+ /** The days of the cycle. */
664
+ days: SigninDay[];
665
+ }
666
+ /** The result of claiming today's grant. */
667
+ interface SigninClaim {
668
+ /** The server's identifier for the claim. */
669
+ claimId: string;
670
+ /** Whether the grant was credited or the day was already claimed. */
671
+ result: SigninClaimResult;
672
+ /** Which day of the cycle was claimed. */
673
+ dayNo: number;
674
+ /** Points granted. */
675
+ points: number;
676
+ /** Extra points granted. */
677
+ bonusPoints: number;
678
+ /** When the granted points expire, in milliseconds since the epoch. */
679
+ expireAtMs: number;
680
+ /** The cycle after the claim. */
681
+ panel?: SigninPanel;
682
+ }
683
+ /**
684
+ * The time zone the server should treat the request as coming from.
685
+ *
686
+ * The server wants an IANA name; an offset or a missing value is refused.
687
+ * The system's own name is the right answer, since "today" should be the
688
+ * user's today.
689
+ */
690
+ declare function timezoneId(): string;
691
+ /**
692
+ * Read the cycle out of a panel response.
693
+ *
694
+ * The server marks exactly one day as today and at most one day as claimable;
695
+ * a panel that says otherwise is not something to act on, so it is refused
696
+ * rather than half-believed.
697
+ */
698
+ declare function parseSigninPanel(document: unknown): SigninPanel | undefined;
699
+ /** Read the result of a claim. */
700
+ declare function parseSigninClaim(document: unknown): SigninClaim | undefined;
701
+ /**
702
+ * Read the cycle.
703
+ *
704
+ * @returns The panel, or `undefined` when the account cannot read it.
705
+ */
706
+ declare function fetchSigninPanel(credential: OAuthCredential, region: MinimaxRegion, fetchImpl?: typeof fetch, timezone?: string): Promise<SigninPanel | undefined>;
707
+ /**
708
+ * Claim today's grant.
709
+ *
710
+ * This is the only request in the plugin that changes something upstream. It
711
+ * grants the day's points, which expire thirty days later.
712
+ *
713
+ * @returns What the server answered, or `undefined` when it refused.
714
+ */
715
+ declare function claimSignin(credential: OAuthCredential, region: MinimaxRegion, fetchImpl?: typeof fetch, timezone?: string): Promise<SigninClaim | undefined>;
716
+ /** The day's key in the account's own time zone, `YYYY-MM-DD`. */
717
+ declare function localDay(now?: number, timezone?: string): string;
718
+ //#endregion
478
719
  //#region src/status-route.d.ts
479
720
  /** Route this plugin mounts, matching its settings card. */
480
721
  declare const MINIMAX_STATUS_PATH = "/plugins/dsh-connect-minimaxcode/status";
722
+ /** The daily check-in, as the card reads it. */
723
+ interface SigninStatus {
724
+ /** Whether the cycle could be read. */
725
+ state: 'ok' | 'unavailable';
726
+ /** Today's day in the cycle. */
727
+ today?: {
728
+ /** Which day of the cycle it is, 1 to 7. */
729
+ dayNo: number;
730
+ /** Points the day grants. */
731
+ points: number;
732
+ /** The day's status, as the upstream numbers it. */
733
+ status: number;
734
+ /** Whether the day can be claimed. */
735
+ claimable: boolean;
736
+ /** Whether the day is already claimed. */
737
+ claimed: boolean;
738
+ };
739
+ /** The days of the cycle. */
740
+ days?: {
741
+ dayNo: number;
742
+ points: number;
743
+ status: number;
744
+ isToday: boolean;
745
+ }[];
746
+ /** Points the whole cycle is worth. */
747
+ cyclePoints?: number;
748
+ /** Whether the plugin claims the day's grant on its own. */
749
+ autoCheckin: boolean;
750
+ /** The day the last claim happened. */
751
+ lastClaimDay?: string;
752
+ /** When the last claim happened, in milliseconds since the epoch. */
753
+ lastClaimAtMs?: number;
754
+ /** Points the last claim granted. */
755
+ lastClaimPoints?: number;
756
+ /** Whether the last claim was credited, or the day was already claimed. */
757
+ lastClaimResult?: number;
758
+ /** Why the cycle or the claim could not be read. */
759
+ error?: string;
760
+ }
761
+ /** How the plugin is signed in, as the card reads it. */
762
+ interface LoginStatus {
763
+ /** Whether the plugin has a usable credential. */
764
+ state: 'signed-in' | 'signed-out' | 'pending';
765
+ /** Where the credential came from. */
766
+ source?: 'plugin' | 'desktop-app';
767
+ /** The code to type on the account page, while a sign-in is waiting. */
768
+ userCode?: string;
769
+ /** Where to authorize. */
770
+ verificationUri?: string;
771
+ /** Where to authorize, with the code filled in. */
772
+ verificationUriComplete?: string;
773
+ /** When the device code stops working, in milliseconds since the epoch. */
774
+ expiresAtMs?: number;
775
+ }
481
776
  /** The account's credit balance, as the desktop app shows it. */
482
777
  interface CreditStatus {
483
778
  /** Whether the balance could be read. */
@@ -552,6 +847,10 @@ interface StatusDocument {
552
847
  credits?: CreditStatus;
553
848
  /** What this host's requests have used. */
554
849
  usage?: UsageStatus;
850
+ /** The daily check-in, when it could be read. */
851
+ signin?: SigninStatus;
852
+ /** How the plugin is signed in. */
853
+ login?: LoginStatus;
555
854
  }
556
855
  /** Inputs the document is derived from. */
557
856
  interface StatusDeps {
@@ -574,6 +873,10 @@ interface StatusDeps {
574
873
  credits?: CreditStatus;
575
874
  /** What this host's requests have used. */
576
875
  usage?: UsageStatus;
876
+ /** The daily check-in, when the account API could be reached. */
877
+ signin?: SigninStatus;
878
+ /** How the plugin is signed in. */
879
+ login?: LoginStatus;
577
880
  }
578
881
  /**
579
882
  * Build the card's status document.
@@ -646,4 +949,4 @@ declare const inject: string[];
646
949
  */
647
950
  declare function apply(ctx: Context): void;
648
951
  //#endregion
649
- export { type AuthState, type CatalogModel, type CatalogSource, type Credential, type CreditSummary, EMPTY_USAGE, FALLBACK_MODELS, MEMBERSHIP_PATH, MINIMAXCODE_PROVIDER_ID, MINIMAX_AUTH_FILENAME, MINIMAX_DATA_DIR, MINIMAX_GATEWAYS, MINIMAX_OAUTH_CLIENT_ID, MINIMAX_REGIONS, MINIMAX_STATUS_PATH, MINIMAX_STREAM_IDLE_TIMEOUT_MS, MODEL_PRICES, MinimaxCatalog, MinimaxCredits, type MinimaxRegion, type ModelUsage, type OAuthCredential, type OAuthState, type StatusDocument, type TokenClaims, UsageMeter, apply, authPath, bearerToken, catalogUrl, chatBaseUrl, createMinimaxCodeAdapter, dataDir, daysRemaining, decodeClaims, ensureOAuthCredential, estimateCny, expiresInMs, expiryLevel, fetchCreditSummary, inject, name, parseAuthDocument, parseCatalog, parseCreditSummary, parseOAuthDocument, priceFor, readCredential, readOAuthCredential, refreshOAuthCredential, statusDocument, writeOAuthCredential };
952
+ export { type AuthState, type CatalogModel, type CatalogSource, type Credential, type CreditSummary, DEVICE_CODE_GRANT, DEVICE_CODE_PATH, type DeviceLoginState, EMPTY_USAGE, FALLBACK_MODELS, MEMBERSHIP_PATH, MINIMAXCODE_PROVIDER_ID, MINIMAX_AUTH_FILENAME, MINIMAX_DATA_DIR, MINIMAX_GATEWAYS, MINIMAX_OAUTH_CLIENT_ID, MINIMAX_REGIONS, MINIMAX_STATUS_PATH, MINIMAX_STREAM_IDLE_TIMEOUT_MS, MODEL_PRICES, MinimaxCatalog, MinimaxCredits, type MinimaxRegion, type ModelUsage, type OAuthCredential, type OAuthState, PLUGIN_CREDENTIAL_FILENAME, PLUGIN_STATE_FILENAME, type PluginCredential, type PluginState, SIGNIN_CLAIM_PATH, SIGNIN_CLAIM_RESULT, SIGNIN_CYCLE_DAYS, SIGNIN_DAY_STATUS, SIGNIN_PANEL_PATH, type SigninClaim, type SigninDay, type SigninPanel, type StatusDocument, type TokenClaims, UsageMeter, apply, authPath, bearerToken, catalogUrl, chatBaseUrl, claimSignin, createMinimaxCodeAdapter, dataDir, daysRemaining, decodeClaims, dshHome, ensureOAuthCredential, estimateCny, expiresInMs, expiryLevel, fetchCreditSummary, fetchSigninPanel, inject, localDay, makePkce, name, parseAuthDocument, parseCatalog, parseCreditSummary, parseOAuthDocument, parseSigninClaim, parseSigninPanel, pluginCredentialPath, pluginStatePath, pollDeviceLogin, priceFor, readCredential, readOAuthCredential, readPluginCredential, readPluginState, refreshOAuthCredential, refreshPluginCredential, startDeviceLogin, statusDocument, timezoneId, writeOAuthCredential, writePluginCredential, writePluginState };