dsh-plugin-subscription-oauth 0.15.0 → 0.16.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/auth/registry.d.ts +28 -0
- package/lib/auth/registry.js +7 -0
- package/lib/auth/rpc.js +6 -0
- package/lib/auth/workbuddy-flow.d.ts +22 -0
- package/lib/auth/workbuddy-flow.js +112 -0
- package/lib/client/SubscriptionsSection.js +40 -5
- package/lib/client/locales.d.ts +8 -0
- package/lib/client/locales.js +8 -0
- package/lib/client.js +64 -14
- package/lib/client.js.map +1 -1
- package/lib/protocol.d.ts +28 -1
- package/lib/protocol.js +1 -0
- package/lib/providers/workbuddy-provider.js +46 -1
- package/package.json +1 -1
package/lib/auth/registry.d.ts
CHANGED
|
@@ -38,6 +38,20 @@ export interface ProviderRoute {
|
|
|
38
38
|
streakDays: number;
|
|
39
39
|
already: boolean;
|
|
40
40
|
}>;
|
|
41
|
+
/** Read growth tasks and optionally auto-claim, when the route has them. */
|
|
42
|
+
tasks?(account: string, claim: boolean): Promise<{
|
|
43
|
+
tasks: {
|
|
44
|
+
taskCode: string;
|
|
45
|
+
title: string;
|
|
46
|
+
credit: number;
|
|
47
|
+
target: number;
|
|
48
|
+
current: number;
|
|
49
|
+
state: 'pending' | 'claimable' | 'claimed' | 'locked';
|
|
50
|
+
}[];
|
|
51
|
+
claimed: number;
|
|
52
|
+
claimable: number;
|
|
53
|
+
creditEarned: number;
|
|
54
|
+
}>;
|
|
41
55
|
/** Fast and image variants for one model, when the route's discovery reported them. */
|
|
42
56
|
modelVariants?(id: string): Promise<ModelVariants | undefined> | ModelVariants | undefined;
|
|
43
57
|
}
|
|
@@ -113,4 +127,18 @@ export declare class SubscriptionRegistry {
|
|
|
113
127
|
streakDays: number;
|
|
114
128
|
already: boolean;
|
|
115
129
|
}>;
|
|
130
|
+
/** Read one account's growth tasks and optionally auto-claim the claimable rewards. */
|
|
131
|
+
tasks(provider: string, account: string, claim: boolean): Promise<{
|
|
132
|
+
tasks: {
|
|
133
|
+
taskCode: string;
|
|
134
|
+
title: string;
|
|
135
|
+
credit: number;
|
|
136
|
+
target: number;
|
|
137
|
+
current: number;
|
|
138
|
+
state: 'pending' | 'claimable' | 'claimed' | 'locked';
|
|
139
|
+
}[];
|
|
140
|
+
claimed: number;
|
|
141
|
+
claimable: number;
|
|
142
|
+
creditEarned: number;
|
|
143
|
+
}>;
|
|
116
144
|
}
|
package/lib/auth/registry.js
CHANGED
|
@@ -168,4 +168,11 @@ export class SubscriptionRegistry {
|
|
|
168
168
|
throw new ProviderError(`${provider} has no check-in`, BAD_REQUEST);
|
|
169
169
|
return route.checkin(account);
|
|
170
170
|
}
|
|
171
|
+
/** Read one account's growth tasks and optionally auto-claim the claimable rewards. */
|
|
172
|
+
async tasks(provider, account, claim) {
|
|
173
|
+
const route = this.route(provider);
|
|
174
|
+
if (route.tasks === undefined)
|
|
175
|
+
throw new ProviderError(`${provider} has no tasks`, BAD_REQUEST);
|
|
176
|
+
return route.tasks(account, claim);
|
|
177
|
+
}
|
|
171
178
|
}
|
package/lib/auth/rpc.js
CHANGED
|
@@ -169,6 +169,12 @@ async function runEndpoint(registry, endpoint, payload, balance) {
|
|
|
169
169
|
throw new ProviderError('checkin requires an account', BAD_REQUEST);
|
|
170
170
|
return registry.checkin(requiredProvider(payload, endpoint), account);
|
|
171
171
|
}
|
|
172
|
+
case 'tasks': {
|
|
173
|
+
const account = stringField(payload, 'account');
|
|
174
|
+
if (account === undefined)
|
|
175
|
+
throw new ProviderError('tasks requires an account', BAD_REQUEST);
|
|
176
|
+
return registry.tasks(requiredProvider(payload, endpoint), account, booleanField(payload, 'claim') === true);
|
|
177
|
+
}
|
|
172
178
|
case 'deepseek-balance': {
|
|
173
179
|
// The DeepSeek account's balance, read on the node half through the host's own
|
|
174
180
|
// account service. The browser cannot call that service directly (it sits behind
|
|
@@ -108,6 +108,28 @@ export interface WorkBuddyCheckin {
|
|
|
108
108
|
streakDays: number;
|
|
109
109
|
todayCredit: number;
|
|
110
110
|
}
|
|
111
|
+
/** One growth task, as the activity endpoint frames it. */
|
|
112
|
+
export interface WorkBuddyTask {
|
|
113
|
+
taskCode: string;
|
|
114
|
+
title: string;
|
|
115
|
+
credit: number;
|
|
116
|
+
energy: number;
|
|
117
|
+
target: number;
|
|
118
|
+
current: number;
|
|
119
|
+
claimable: boolean;
|
|
120
|
+
claimed: boolean;
|
|
121
|
+
locked: boolean;
|
|
122
|
+
acceptStatus: string;
|
|
123
|
+
}
|
|
124
|
+
/** Read the account's growth tasks, parsed the way the pool plugin parses them. */
|
|
125
|
+
export declare function listWorkBuddyTasks(token: WorkBuddyTokenSet, fetchFn: typeof fetch): Promise<WorkBuddyTask[]>;
|
|
126
|
+
/** Accept (enrol in) tasks by code; replaying an accepted task is a safe no-op. */
|
|
127
|
+
export declare function acceptWorkBuddyTasks(token: WorkBuddyTokenSet, taskCodes: readonly string[], fetchFn: typeof fetch): Promise<void>;
|
|
128
|
+
/** Claim one task's reward; already-claimed answers zero and counts as success. */
|
|
129
|
+
export declare function claimWorkBuddyTask(token: WorkBuddyTokenSet, taskCode: string, fetchFn: typeof fetch): Promise<{
|
|
130
|
+
credit: number;
|
|
131
|
+
energy: number;
|
|
132
|
+
}>;
|
|
111
133
|
/** Read the account's daily check-in state, without changing anything. */
|
|
112
134
|
export declare function readWorkBuddyCheckin(token: WorkBuddyTokenSet, fetchFn: typeof fetch): Promise<WorkBuddyCheckin>;
|
|
113
135
|
/** Claim today's check-in reward; resolves with the credit granted and the streak. */
|
|
@@ -263,6 +263,118 @@ function workbuddyEnterpriseHeaders(token) {
|
|
|
263
263
|
const enterprise = typeof token.enterpriseId === 'string' ? token.enterpriseId.trim() : '';
|
|
264
264
|
return enterprise === '' ? {} : { 'X-Enterprise-Id': enterprise, 'X-Tenant-Id': enterprise };
|
|
265
265
|
}
|
|
266
|
+
/** The chat request headers: the account's, plus the CLI conventions. */
|
|
267
|
+
function chatHeaders(token) {
|
|
268
|
+
return {
|
|
269
|
+
'Accept': 'application/json, text/plain, */*',
|
|
270
|
+
'X-Requested-With': 'XMLHttpRequest',
|
|
271
|
+
'Origin': 'https://copilot.tencent.com',
|
|
272
|
+
'Referer': 'https://copilot.tencent.com/',
|
|
273
|
+
'User-Agent': 'CLI/2.63.2 CodeBuddy/2.63.2',
|
|
274
|
+
'Content-Type': 'application/json',
|
|
275
|
+
'Authorization': `Bearer ${token.accessToken}`,
|
|
276
|
+
...token.uid === undefined || token.uid === '' ? { 'X-No-User-Id': '1' } : { 'X-User-Id': token.uid },
|
|
277
|
+
...token.enterpriseId === undefined || token.enterpriseId === '' ? { 'X-No-Enterprise-Id': '1' } : { 'X-Enterprise-Id': token.enterpriseId },
|
|
278
|
+
'X-Product': 'SaaS',
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
/** Ask the chat host one question, returning the decoded envelope's data. */
|
|
282
|
+
async function chatCall(token, method, path, body, fetchFn) {
|
|
283
|
+
const response = await fetchFn(`https://copilot.tencent.com${path}`, {
|
|
284
|
+
method,
|
|
285
|
+
headers: chatHeaders(token),
|
|
286
|
+
...body === undefined ? {} : { body: JSON.stringify(body) },
|
|
287
|
+
signal: AbortSignal.timeout(20_000),
|
|
288
|
+
});
|
|
289
|
+
let env = {};
|
|
290
|
+
try {
|
|
291
|
+
env = JSON.parse(await response.text());
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
env = {};
|
|
295
|
+
}
|
|
296
|
+
const code = Number.parseInt(String(env.code ?? ''), 10) || 0;
|
|
297
|
+
if (!response.ok || code !== 0) {
|
|
298
|
+
throw new Error(`WorkBuddy ${path}: ${String(env.msg ?? `error ${code}`)}`);
|
|
299
|
+
}
|
|
300
|
+
return isRecord(env.data) ? env.data : null;
|
|
301
|
+
}
|
|
302
|
+
/** Read the account's growth tasks, parsed the way the pool plugin parses them. */
|
|
303
|
+
export async function listWorkBuddyTasks(token, fetchFn) {
|
|
304
|
+
const data = await chatCall(token, 'GET', '/v2/activity/growth/tasks', undefined, fetchFn);
|
|
305
|
+
if (data === null)
|
|
306
|
+
return [];
|
|
307
|
+
const raw = Array.isArray(data.tasks) ? data.tasks.filter(isRecord) : [];
|
|
308
|
+
const out = [];
|
|
309
|
+
for (const entry of raw) {
|
|
310
|
+
const taskCode = typeof entry.task_code === 'string' ? entry.task_code : '';
|
|
311
|
+
if (taskCode === '')
|
|
312
|
+
continue;
|
|
313
|
+
const num = (key) => capacity(entry[key]);
|
|
314
|
+
let current = num('current');
|
|
315
|
+
let target = num('target');
|
|
316
|
+
if (isRecord(entry.progress)) {
|
|
317
|
+
const nestedCurrent = capacity(entry.progress.current);
|
|
318
|
+
const nestedTarget = capacity(entry.progress.target);
|
|
319
|
+
if (nestedTarget > 0 || nestedCurrent > 0) {
|
|
320
|
+
current = nestedCurrent;
|
|
321
|
+
target = nestedTarget;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const acceptStatus = typeof entry.accept_status === 'string' ? entry.accept_status : '';
|
|
325
|
+
const claimed = acceptStatus === 'claimed';
|
|
326
|
+
out.push({
|
|
327
|
+
taskCode,
|
|
328
|
+
title: typeof entry.title === 'string' && entry.title !== '' ? entry.title : taskCode,
|
|
329
|
+
credit: num('reward_credit'),
|
|
330
|
+
energy: num('reward_energy'),
|
|
331
|
+
target,
|
|
332
|
+
current,
|
|
333
|
+
claimable: !claimed && target > 0 && current >= target,
|
|
334
|
+
claimed,
|
|
335
|
+
locked: entry.locked === true,
|
|
336
|
+
acceptStatus,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
return out;
|
|
340
|
+
}
|
|
341
|
+
/** Accept (enrol in) tasks by code; replaying an accepted task is a safe no-op. */
|
|
342
|
+
export async function acceptWorkBuddyTasks(token, taskCodes, fetchFn) {
|
|
343
|
+
if (taskCodes.length === 0)
|
|
344
|
+
return;
|
|
345
|
+
await chatCall(token, 'POST', '/v2/activity/growth/tasks/accept', { task_codes: [...taskCodes] }, fetchFn);
|
|
346
|
+
}
|
|
347
|
+
/** Claim one task's reward; already-claimed answers zero and counts as success. */
|
|
348
|
+
export async function claimWorkBuddyTask(token, taskCode, fetchFn) {
|
|
349
|
+
const headers = {
|
|
350
|
+
...workbuddyHeaders(token),
|
|
351
|
+
'Accept': 'application/json, text/plain, */*',
|
|
352
|
+
'Origin': 'https://www.workbuddy.cn',
|
|
353
|
+
'Referer': 'https://www.workbuddy.cn/profile/growth-center',
|
|
354
|
+
'x-client-platform': 'web',
|
|
355
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36',
|
|
356
|
+
};
|
|
357
|
+
const response = await fetchFn(`https://www.workbuddy.cn/activity/growth/tasks/${encodeURIComponent(taskCode)}/claim`, {
|
|
358
|
+
method: 'POST',
|
|
359
|
+
headers,
|
|
360
|
+
signal: AbortSignal.timeout(20_000),
|
|
361
|
+
});
|
|
362
|
+
let env = {};
|
|
363
|
+
try {
|
|
364
|
+
env = JSON.parse(await response.text());
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
env = {};
|
|
368
|
+
}
|
|
369
|
+
const code = Number.parseInt(String(env.code ?? ''), 10) || 0;
|
|
370
|
+
if (!response.ok || code !== 0) {
|
|
371
|
+
throw new Error(`WorkBuddy task ${taskCode}: ${String(env.msg ?? `error ${code}`)}`);
|
|
372
|
+
}
|
|
373
|
+
const data = isRecord(env.data) ? env.data : {};
|
|
374
|
+
if (data.already_claimed === true)
|
|
375
|
+
return { credit: 0, energy: 0 };
|
|
376
|
+
return { credit: capacity(data.credit), energy: capacity(data.energy) };
|
|
377
|
+
}
|
|
266
378
|
/** A capacity the billing API sends, as a string or number; empty is 0. */
|
|
267
379
|
function capacity(value) {
|
|
268
380
|
if (value === null || value === undefined || value === '')
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* Subscriptions settings page.
|
|
4
4
|
*
|
|
@@ -315,11 +315,15 @@ function windowAmount(window) {
|
|
|
315
315
|
}
|
|
316
316
|
/** One account's metered windows and check-in, rendered under its account row. */
|
|
317
317
|
function AccountUsage(props) {
|
|
318
|
-
const { entry, t, busyAction, onCheckin } = props;
|
|
318
|
+
const { entry, t, busyAction, onCheckin, onTasks } = props;
|
|
319
319
|
if (entry === undefined)
|
|
320
320
|
return null;
|
|
321
321
|
const checkin = entry.checkin;
|
|
322
|
-
return (_jsxs("div", { style: styles.usageInline, children: [
|
|
322
|
+
return (_jsxs("div", { style: styles.usageInline, children: [_jsxs("div", { style: styles.usageLine, children: [_jsx("span", { style: styles.modelName, children: checkin?.active === true
|
|
323
|
+
? checkin.todayCheckedIn
|
|
324
|
+
? t('checkinDone', { days: checkin.streakDays })
|
|
325
|
+
: t('checkinPending')
|
|
326
|
+
: '' }), _jsxs("span", { style: styles.pushRight, children: [onTasks !== undefined && (_jsx("button", { type: "button", className: "dsh-subscription-quiet", disabled: busyAction, onClick: onTasks, children: t('tasksClaim') })), checkin?.active === true && !checkin.todayCheckedIn && onCheckin !== undefined && (_jsx("button", { type: "button", className: "dsh-subscription-quiet", disabled: busyAction, onClick: onCheckin, children: t('checkinClaim') }))] })] }), entry.windows.map((window) => (_jsxs("div", { style: styles.usageRow, children: [_jsxs("div", { style: styles.usageLine, children: [_jsxs("span", { style: styles.modelName, children: [windowLabel(t, window), ' · ', windowAmount(window)] }), _jsx("span", { style: styles.modelMeta, children: window.resetsAt === undefined ? '' : formatReset(window.resetsAt, t) })] }), window.usedPercent !== undefined && (_jsx("div", { className: "dsh-subscription-meter", children: _jsx("span", { style: { width: `${String(Math.min(100, Math.max(0, window.usedPercent)))}%` }, "data-exhausted": window.usedPercent >= 90 ? 'true' : 'false' }) }))] }, `${entry.account}-${window.label}`))), entry.windows.length === 0 && (_jsx("p", { style: styles.statusLine, children: t('usageNone') }))] }));
|
|
323
327
|
}
|
|
324
328
|
/** The plan-and-usage block for the whole route. */
|
|
325
329
|
function UsageBlock(props) {
|
|
@@ -524,7 +528,7 @@ function ProviderCard(props) {
|
|
|
524
528
|
const usageEntry = (status.usage ?? []).find((entry) => entry.account === account.account);
|
|
525
529
|
return (_jsxs("div", { style: styles.accountRow, children: [_jsxs("div", { style: styles.accountHeader, children: [_jsx("button", { type: "button", style: styles.starButton, title: account.isDefault ? t('defaultBadge') : t('makeDefault'), "aria-label": account.isDefault ? t('defaultBadge') : t('makeDefault'), disabled: busyAction || account.isDefault, onClick: () => { props.onSetDefault(account.account); }, children: account.isDefault ? '★' : '☆' }), _jsx("span", { style: styles.accountName, children: account.label }), account.isDefault && _jsx("span", { style: styles.badgeDefault, children: t('defaultBadge') }), account.needsLogin === true && (_jsx("span", { style: styles.badgeWarn, title: t('accountNeedsLoginHint'), children: t('accountNeedsLogin') })), usageEntry?.plan !== undefined && (_jsx("span", { style: styles.badge, children: t('usagePlan', { plan: usageEntry.plan }) })), _jsx("span", { style: styles.statusLine, children: account.expired
|
|
526
530
|
? t('accountExpired')
|
|
527
|
-
: t('accountExpires', { time: formatTime(account.expiresAt) }) }), _jsx("button", { type: "button", className: "dsh-subscription-quiet", style: styles.pushRight, disabled: busyAction, onClick: () => { props.onLogout(account.account); }, children: t('logout') })] }), _jsx(AccountUsage, { entry: usageEntry, t: t, busyAction: busyAction, ...props.onCheckin === undefined ? {} : { onCheckin: () => { props.onCheckin?.(account.account); } } })] }, account.account));
|
|
531
|
+
: t('accountExpires', { time: formatTime(account.expiresAt) }) }), _jsx("button", { type: "button", className: "dsh-subscription-quiet", style: styles.pushRight, disabled: busyAction, onClick: () => { props.onLogout(account.account); }, children: t('logout') })] }), _jsx(AccountUsage, { entry: usageEntry, t: t, busyAction: busyAction, ...props.onCheckin === undefined ? {} : { onCheckin: () => { props.onCheckin?.(account.account); } }, ...props.onTasks === undefined ? {} : { onTasks: () => { props.onTasks?.(account.account); } } })] }, account.account));
|
|
528
532
|
}), status.loggedIn && (_jsxs("div", { style: styles.catalog, children: [_jsxs("div", { style: styles.catalogHeader, children: [_jsxs("button", { type: "button", className: "dsh-subscription-disclosure", style: styles.disclosure, "aria-expanded": modelsOpen, "aria-controls": `dsh-subscription-models-${status.provider}`, onClick: toggleModelsOpen, children: [_jsx("span", { style: styles.chevron, "aria-hidden": "true", children: modelsOpen ? '▾' : '▸' }), _jsx("span", { style: styles.catalogTitle, children: t('modelsTitle') })] }), _jsxs("span", { style: styles.catalogMeta, children: [t('modelsCount', { count: models.length }), ' · ', liveCatalog ? t('modelsLive') : t('modelsBuiltIn'), hiddenCount === 0 ? '' : ` ${t('modelsHiddenCount', { count: hiddenCount })}`] }), _jsx("button", { type: "button", className: "dsh-subscription-quiet", style: styles.pushRight, disabled: busyAction, onClick: props.onRefreshModels, children: busyAction ? t('refreshing') : t('refresh') }), _jsx("button", { type: "button", className: "dsh-subscription-quiet", disabled: busyAction || !connectable, title: t('modelsAddHint'), onClick: () => { setAdding((open) => !open); }, children: t('modelsAdd') })] }), adding && (_jsxs("form", { style: styles.addRow, onSubmit: (event) => {
|
|
529
533
|
event.preventDefault();
|
|
530
534
|
const id = draft.trim();
|
|
@@ -646,6 +650,37 @@ export function SubscriptionsSection(props) {
|
|
|
646
650
|
setBusyAction(false);
|
|
647
651
|
}
|
|
648
652
|
}, [call, refresh, t]);
|
|
653
|
+
/** Auto-claim one account's growth-task rewards, then refresh the figures. */
|
|
654
|
+
const runTasks = useCallback(async (provider, account) => {
|
|
655
|
+
if (call === undefined)
|
|
656
|
+
return;
|
|
657
|
+
setBusyAction(true);
|
|
658
|
+
try {
|
|
659
|
+
const result = await call('tasks', { provider, account, claim: true });
|
|
660
|
+
if (!mounted.current)
|
|
661
|
+
return;
|
|
662
|
+
setMessages((previous) => ({
|
|
663
|
+
...previous,
|
|
664
|
+
[provider]: {
|
|
665
|
+
tone: 'success',
|
|
666
|
+
text: result.claimed > 0
|
|
667
|
+
? t('tasksClaimed', { count: result.claimed, credit: result.creditEarned })
|
|
668
|
+
: result.claimable > 0
|
|
669
|
+
? t('tasksClaimable', { count: result.claimable })
|
|
670
|
+
: t('tasksNone'),
|
|
671
|
+
},
|
|
672
|
+
}));
|
|
673
|
+
await refresh();
|
|
674
|
+
}
|
|
675
|
+
catch (caught) {
|
|
676
|
+
if (mounted.current)
|
|
677
|
+
setError(t('actionFailed', { message: messageOf(caught) }));
|
|
678
|
+
}
|
|
679
|
+
finally {
|
|
680
|
+
if (mounted.current)
|
|
681
|
+
setBusyAction(false);
|
|
682
|
+
}
|
|
683
|
+
}, [call, refresh, t]);
|
|
649
684
|
const stopPolling = useCallback(() => {
|
|
650
685
|
if (timer.current !== undefined) {
|
|
651
686
|
clearInterval(timer.current);
|
|
@@ -889,5 +924,5 @@ export function SubscriptionsSection(props) {
|
|
|
889
924
|
}, [act, call]);
|
|
890
925
|
if (call === undefined)
|
|
891
926
|
return _jsx("p", { style: styles.intro, children: t('unavailable') });
|
|
892
|
-
return (_jsxs("div", { className: "dsh-subscriptions-settings", style: styles.section, children: [_jsx("style", { children: subscriptionsCss }), _jsxs("div", { style: styles.sectionHeader, children: [_jsx("h2", { style: styles.heading, children: t('nav') }), _jsx("p", { style: styles.intro, children: t('intro') })] }), error !== undefined && _jsx("p", { className: "dsh-subscription-error", children: error }), status === undefined && _jsx("p", { style: styles.statusLine, children: t('loading') }), orderedProviders.map((provider) => (_jsx(ProviderCard, { status: provider, t: t, busyAction: busyAction, ...messages[provider.provider] === undefined ? {} : { message: messages[provider.provider] }, onLogin: () => { login(provider.provider); }, onCancel: () => { cancel(provider.provider); }, onLogout: (account) => { logout(provider.provider, account); }, onSetDefault: (account) => { setDefault(provider.provider, account); }, onRefreshToken: (account) => { refreshToken(provider.provider, account); }, onRefreshModels: () => { refreshModels(provider.provider); }, onSetModelVisible: (id, visible) => { setModelVisible(provider.provider, id, visible); }, onSetModelOrder: (ids) => { setModelOrder(provider.provider, ids); }, onAddModel: (id) => { addModel(provider.provider, id); }, onRemoveModel: (id) => { removeModel(provider.provider, id); }, onRefreshUsage: () => { refreshUsage(provider.provider); }, onReload: () => { reload(provider.provider); }, ...provider.provider !== 'auto' ? {} : { autoOn, onSetAutoOn: (enabled) => { void setAuto(enabled); } }, ...provider.provider !== 'workbuddy' ? {} : { onCheckin: (account) => { void checkin(provider.provider, account); } } }, provider.provider)))] }));
|
|
927
|
+
return (_jsxs("div", { className: "dsh-subscriptions-settings", style: styles.section, children: [_jsx("style", { children: subscriptionsCss }), _jsxs("div", { style: styles.sectionHeader, children: [_jsx("h2", { style: styles.heading, children: t('nav') }), _jsx("p", { style: styles.intro, children: t('intro') })] }), error !== undefined && _jsx("p", { className: "dsh-subscription-error", children: error }), status === undefined && _jsx("p", { style: styles.statusLine, children: t('loading') }), orderedProviders.map((provider) => (_jsx(ProviderCard, { status: provider, t: t, busyAction: busyAction, ...messages[provider.provider] === undefined ? {} : { message: messages[provider.provider] }, onLogin: () => { login(provider.provider); }, onCancel: () => { cancel(provider.provider); }, onLogout: (account) => { logout(provider.provider, account); }, onSetDefault: (account) => { setDefault(provider.provider, account); }, onRefreshToken: (account) => { refreshToken(provider.provider, account); }, onRefreshModels: () => { refreshModels(provider.provider); }, onSetModelVisible: (id, visible) => { setModelVisible(provider.provider, id, visible); }, onSetModelOrder: (ids) => { setModelOrder(provider.provider, ids); }, onAddModel: (id) => { addModel(provider.provider, id); }, onRemoveModel: (id) => { removeModel(provider.provider, id); }, onRefreshUsage: () => { refreshUsage(provider.provider); }, onReload: () => { reload(provider.provider); }, ...provider.provider !== 'auto' ? {} : { autoOn, onSetAutoOn: (enabled) => { void setAuto(enabled); } }, ...provider.provider !== 'workbuddy' ? {} : { onCheckin: (account) => { void checkin(provider.provider, account); } }, ...provider.provider !== 'workbuddy' ? {} : { onTasks: (account) => { void runTasks(provider.provider, account); } } }, provider.provider)))] }));
|
|
893
928
|
}
|
package/lib/client/locales.d.ts
CHANGED
|
@@ -67,6 +67,10 @@ export declare const en: {
|
|
|
67
67
|
readonly checkinClaim: "Check in";
|
|
68
68
|
readonly checkinClaimed: "Checked in, +{credit} credits (streak {days}d)";
|
|
69
69
|
readonly checkinAlready: "Already checked in today (streak {days}d)";
|
|
70
|
+
readonly tasksClaim: "Tasks";
|
|
71
|
+
readonly tasksClaimed: "Claimed {count} task rewards, +{credit} credits";
|
|
72
|
+
readonly tasksClaimable: "{count} tasks ready to claim but not yet earned";
|
|
73
|
+
readonly tasksNone: "No task rewards to claim right now";
|
|
70
74
|
readonly modelsPinned: "pinned";
|
|
71
75
|
readonly usageTitle: "Plan and usage";
|
|
72
76
|
readonly usagePlan: "Plan: {plan}";
|
|
@@ -171,6 +175,10 @@ export declare const zh: {
|
|
|
171
175
|
checkinClaim: string;
|
|
172
176
|
checkinClaimed: string;
|
|
173
177
|
checkinAlready: string;
|
|
178
|
+
tasksClaim: string;
|
|
179
|
+
tasksClaimed: string;
|
|
180
|
+
tasksClaimable: string;
|
|
181
|
+
tasksNone: string;
|
|
174
182
|
modelsPinned: string;
|
|
175
183
|
usageTitle: string;
|
|
176
184
|
usagePlan: string;
|
package/lib/client/locales.js
CHANGED
|
@@ -67,6 +67,10 @@ export const en = {
|
|
|
67
67
|
checkinClaim: 'Check in',
|
|
68
68
|
checkinClaimed: 'Checked in, +{credit} credits (streak {days}d)',
|
|
69
69
|
checkinAlready: 'Already checked in today (streak {days}d)',
|
|
70
|
+
tasksClaim: 'Tasks',
|
|
71
|
+
tasksClaimed: 'Claimed {count} task rewards, +{credit} credits',
|
|
72
|
+
tasksClaimable: '{count} tasks ready to claim but not yet earned',
|
|
73
|
+
tasksNone: 'No task rewards to claim right now',
|
|
70
74
|
modelsPinned: 'pinned',
|
|
71
75
|
usageTitle: 'Plan and usage',
|
|
72
76
|
usagePlan: 'Plan: {plan}',
|
|
@@ -169,6 +173,10 @@ export const zh = {
|
|
|
169
173
|
checkinClaim: '签到',
|
|
170
174
|
checkinClaimed: '签到成功,+{credit} 积分(连签 {days} 天)',
|
|
171
175
|
checkinAlready: '今日已签到(连签 {days} 天)',
|
|
176
|
+
tasksClaim: '做任务',
|
|
177
|
+
tasksClaimed: '已领取 {count} 个任务奖励,+{credit} 积分',
|
|
178
|
+
tasksClaimable: '有 {count} 个任务可领但还未达成',
|
|
179
|
+
tasksNone: '现在没有可领的任务奖励',
|
|
172
180
|
modelsPinned: '固定',
|
|
173
181
|
usageTitle: '套餐与额度',
|
|
174
182
|
usagePlan: '套餐:{plan}',
|
package/lib/client.js
CHANGED
|
@@ -98,6 +98,10 @@ const en = {
|
|
|
98
98
|
checkinClaim: "Check in",
|
|
99
99
|
checkinClaimed: "Checked in, +{credit} credits (streak {days}d)",
|
|
100
100
|
checkinAlready: "Already checked in today (streak {days}d)",
|
|
101
|
+
tasksClaim: "Tasks",
|
|
102
|
+
tasksClaimed: "Claimed {count} task rewards, +{credit} credits",
|
|
103
|
+
tasksClaimable: "{count} tasks ready to claim but not yet earned",
|
|
104
|
+
tasksNone: "No task rewards to claim right now",
|
|
101
105
|
modelsPinned: "pinned",
|
|
102
106
|
usageTitle: "Plan and usage",
|
|
103
107
|
usagePlan: "Plan: {plan}",
|
|
@@ -200,6 +204,10 @@ const zh = {
|
|
|
200
204
|
checkinClaim: "签到",
|
|
201
205
|
checkinClaimed: "签到成功,+{credit} 积分(连签 {days} 天)",
|
|
202
206
|
checkinAlready: "今日已签到(连签 {days} 天)",
|
|
207
|
+
tasksClaim: "做任务",
|
|
208
|
+
tasksClaimed: "已领取 {count} 个任务奖励,+{credit} 积分",
|
|
209
|
+
tasksClaimable: "有 {count} 个任务可领但还未达成",
|
|
210
|
+
tasksNone: "现在没有可领的任务奖励",
|
|
203
211
|
modelsPinned: "固定",
|
|
204
212
|
usageTitle: "套餐与额度",
|
|
205
213
|
usagePlan: "套餐:{plan}",
|
|
@@ -1341,28 +1349,32 @@ function windowAmount$1(window$1) {
|
|
|
1341
1349
|
}
|
|
1342
1350
|
/** One account's metered windows and check-in, rendered under its account row. */
|
|
1343
1351
|
function AccountUsage(props) {
|
|
1344
|
-
const { entry, t, busyAction, onCheckin } = props;
|
|
1352
|
+
const { entry, t, busyAction, onCheckin, onTasks } = props;
|
|
1345
1353
|
if (entry === void 0) return null;
|
|
1346
1354
|
const checkin = entry.checkin;
|
|
1347
1355
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1348
1356
|
style: styles.usageInline,
|
|
1349
1357
|
children: [
|
|
1350
|
-
|
|
1358
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1351
1359
|
style: styles.usageLine,
|
|
1352
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1360
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1353
1361
|
style: styles.modelName,
|
|
1354
|
-
children:
|
|
1355
|
-
|
|
1356
|
-
" · ",
|
|
1357
|
-
checkin.todayCheckedIn ? t("checkinDone", { days: checkin.streakDays }) : t("checkinPending")
|
|
1358
|
-
]
|
|
1359
|
-
}), !checkin.todayCheckedIn && onCheckin !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1360
|
-
type: "button",
|
|
1361
|
-
className: "dsh-subscription-quiet",
|
|
1362
|
+
children: checkin?.active === true ? checkin.todayCheckedIn ? t("checkinDone", { days: checkin.streakDays }) : t("checkinPending") : ""
|
|
1363
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1362
1364
|
style: styles.pushRight,
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1365
|
+
children: [onTasks !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1366
|
+
type: "button",
|
|
1367
|
+
className: "dsh-subscription-quiet",
|
|
1368
|
+
disabled: busyAction,
|
|
1369
|
+
onClick: onTasks,
|
|
1370
|
+
children: t("tasksClaim")
|
|
1371
|
+
}), checkin?.active === true && !checkin.todayCheckedIn && onCheckin !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1372
|
+
type: "button",
|
|
1373
|
+
className: "dsh-subscription-quiet",
|
|
1374
|
+
disabled: busyAction,
|
|
1375
|
+
onClick: onCheckin,
|
|
1376
|
+
children: t("checkinClaim")
|
|
1377
|
+
})]
|
|
1366
1378
|
})]
|
|
1367
1379
|
}),
|
|
1368
1380
|
entry.windows.map((window$1) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1741,6 +1753,9 @@ function ProviderCard(props) {
|
|
|
1741
1753
|
busyAction,
|
|
1742
1754
|
...props.onCheckin === void 0 ? {} : { onCheckin: () => {
|
|
1743
1755
|
props.onCheckin?.(account.account);
|
|
1756
|
+
} },
|
|
1757
|
+
...props.onTasks === void 0 ? {} : { onTasks: () => {
|
|
1758
|
+
props.onTasks?.(account.account);
|
|
1744
1759
|
} }
|
|
1745
1760
|
})]
|
|
1746
1761
|
}, account.account);
|
|
@@ -2181,6 +2196,38 @@ function SubscriptionsSection(props) {
|
|
|
2181
2196
|
refresh,
|
|
2182
2197
|
t
|
|
2183
2198
|
]);
|
|
2199
|
+
/** Auto-claim one account's growth-task rewards, then refresh the figures. */
|
|
2200
|
+
const runTasks = (0, react.useCallback)(async (provider, account) => {
|
|
2201
|
+
if (call === void 0) return;
|
|
2202
|
+
setBusyAction(true);
|
|
2203
|
+
try {
|
|
2204
|
+
const result = await call("tasks", {
|
|
2205
|
+
provider,
|
|
2206
|
+
account,
|
|
2207
|
+
claim: true
|
|
2208
|
+
});
|
|
2209
|
+
if (!mounted.current) return;
|
|
2210
|
+
setMessages((previous) => ({
|
|
2211
|
+
...previous,
|
|
2212
|
+
[provider]: {
|
|
2213
|
+
tone: "success",
|
|
2214
|
+
text: result.claimed > 0 ? t("tasksClaimed", {
|
|
2215
|
+
count: result.claimed,
|
|
2216
|
+
credit: result.creditEarned
|
|
2217
|
+
}) : result.claimable > 0 ? t("tasksClaimable", { count: result.claimable }) : t("tasksNone")
|
|
2218
|
+
}
|
|
2219
|
+
}));
|
|
2220
|
+
await refresh();
|
|
2221
|
+
} catch (caught) {
|
|
2222
|
+
if (mounted.current) setError(t("actionFailed", { message: messageOf(caught) }));
|
|
2223
|
+
} finally {
|
|
2224
|
+
if (mounted.current) setBusyAction(false);
|
|
2225
|
+
}
|
|
2226
|
+
}, [
|
|
2227
|
+
call,
|
|
2228
|
+
refresh,
|
|
2229
|
+
t
|
|
2230
|
+
]);
|
|
2184
2231
|
const stopPolling = (0, react.useCallback)(() => {
|
|
2185
2232
|
if (timer.current !== void 0) {
|
|
2186
2233
|
clearInterval(timer.current);
|
|
@@ -2503,6 +2550,9 @@ function SubscriptionsSection(props) {
|
|
|
2503
2550
|
},
|
|
2504
2551
|
...provider.provider !== "workbuddy" ? {} : { onCheckin: (account) => {
|
|
2505
2552
|
checkin(provider.provider, account);
|
|
2553
|
+
} },
|
|
2554
|
+
...provider.provider !== "workbuddy" ? {} : { onTasks: (account) => {
|
|
2555
|
+
runTasks(provider.provider, account);
|
|
2506
2556
|
} }
|
|
2507
2557
|
}, provider.provider))
|
|
2508
2558
|
]
|