linkshell-cli 0.10.45 → 0.10.47
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/README.md +2 -0
- package/dist/cli/src/auth.d.ts +6 -2
- package/dist/cli/src/auth.js +12 -4
- package/dist/cli/src/auth.js.map +1 -1
- package/dist/cli/src/commands/desktop-workspace-state.d.ts +19 -0
- package/dist/cli/src/commands/desktop-workspace-state.js +92 -0
- package/dist/cli/src/commands/desktop-workspace-state.js.map +1 -0
- package/dist/cli/src/commands/desktop-workspace.d.ts +12 -0
- package/dist/cli/src/commands/desktop-workspace.js +415 -0
- package/dist/cli/src/commands/desktop-workspace.js.map +1 -0
- package/dist/cli/src/commands/desktop.d.ts +5 -5
- package/dist/cli/src/commands/desktop.js +55 -5
- package/dist/cli/src/commands/desktop.js.map +1 -1
- package/dist/cli/src/commands/login.d.ts +3 -1
- package/dist/cli/src/commands/login.js +44 -26
- package/dist/cli/src/commands/login.js.map +1 -1
- package/dist/cli/src/index.js +15 -0
- package/dist/cli/src/index.js.map +1 -1
- package/dist/cli/src/pro-membership.d.ts +16 -0
- package/dist/cli/src/pro-membership.js +91 -0
- package/dist/cli/src/pro-membership.js.map +1 -0
- package/dist/cli/tsconfig.tsbuildinfo +1 -1
- package/dist/client-core/src/pro-membership.d.ts +18 -0
- package/dist/client-core/src/pro-membership.js +109 -0
- package/dist/client-core/src/pro-membership.js.map +1 -0
- package/dist/host/src/input.d.ts +29 -2
- package/dist/host/src/input.js +99 -4
- package/dist/host/src/input.js.map +1 -1
- package/dist/host/src/screen-session.d.ts +35 -0
- package/dist/host/src/screen-session.js +26 -0
- package/dist/host/src/screen-session.js.map +1 -0
- package/dist/host/src/screen-viewer.js +293 -30
- package/dist/host/src/screen-viewer.js.map +1 -1
- package/dist/host/src/screen.d.ts +3 -1
- package/dist/host/src/screen.js +454 -160
- package/dist/host/src/screen.js.map +1 -1
- package/package.json +6 -4
- package/src/auth.ts +9 -4
- package/src/commands/desktop-workspace-state.ts +87 -0
- package/src/commands/desktop-workspace.ts +353 -0
- package/src/commands/desktop.ts +49 -5
- package/src/commands/login.ts +33 -34
- package/src/index.ts +16 -0
- package/src/pro-membership.ts +82 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { fetchProMembership, redeemProCode } from "../../client-core/src/pro-membership.js";
|
|
4
|
+
import { getValidToken, loadAuth, SUPABASE_ANON_KEY, SUPABASE_URL } from "./auth.js";
|
|
5
|
+
import { linkshellDir } from "./utils/daemon.js";
|
|
6
|
+
|
|
7
|
+
export interface DesktopMembership {
|
|
8
|
+
plan: "pro" | "free" | "unknown";
|
|
9
|
+
planExpiresAt: string | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const unknownMembership: DesktopMembership = { plan: "unknown", planExpiresAt: null };
|
|
13
|
+
const changedAccount = "登录账号已变更,请刷新账号后重试";
|
|
14
|
+
const cachePath = () => join(linkshellDir(), "account-pro.json");
|
|
15
|
+
|
|
16
|
+
/** Snapshot polling stays offline; the dashboard refreshes this account-scoped cache separately. */
|
|
17
|
+
export function cachedMembership(userId: string): DesktopMembership {
|
|
18
|
+
try {
|
|
19
|
+
const value = JSON.parse(readFileSync(cachePath(), "utf8"));
|
|
20
|
+
if (value.userId !== userId || !["pro", "free", "unknown"].includes(value.plan)) return { ...unknownMembership };
|
|
21
|
+
const expiry = typeof value.planExpiresAt === "string" && Number.isFinite(Date.parse(value.planExpiresAt)) ? value.planExpiresAt : null;
|
|
22
|
+
if (value.plan === "pro" && !expiry) return { ...unknownMembership };
|
|
23
|
+
return { plan: value.plan === "pro" ? expiry && Date.parse(expiry) > Date.now() ? "pro" : "free" : value.plan, planExpiresAt: expiry };
|
|
24
|
+
} catch {
|
|
25
|
+
return { ...unknownMembership };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function cacheMembership(userId: string, membership: DesktopMembership, observedAt = Date.now()): void {
|
|
30
|
+
try {
|
|
31
|
+
const previous = JSON.parse(readFileSync(cachePath(), "utf8"));
|
|
32
|
+
// A profile request begun before redemption cannot replace its confirmed result.
|
|
33
|
+
if (previous.userId === userId && previous.observedAt > observedAt) return;
|
|
34
|
+
} catch { /* There may not be a prior account observation. */ }
|
|
35
|
+
mkdirSync(linkshellDir(), { recursive: true });
|
|
36
|
+
writeFileSync(cachePath(), JSON.stringify({ userId, ...membership, observedAt }) + "\n", { encoding: "utf8", mode: 0o600 });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function accountToken(expectedUserId?: string): Promise<{ userId: string; accessToken: string }> {
|
|
40
|
+
const auth = loadAuth();
|
|
41
|
+
if (!auth?.userId) throw new Error("请先登录账号");
|
|
42
|
+
if (expectedUserId !== undefined && auth.userId !== expectedUserId) throw new Error(changedAccount);
|
|
43
|
+
const accessToken = await getValidToken();
|
|
44
|
+
const current = loadAuth();
|
|
45
|
+
if (current?.userId !== auth.userId) throw new Error(changedAccount);
|
|
46
|
+
if (!accessToken || current.accessToken !== accessToken) throw new Error("登录已失效,请重新登录后再试");
|
|
47
|
+
return { userId: auth.userId, accessToken };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function refreshMembership(): Promise<DesktopMembership> {
|
|
51
|
+
const userId = loadAuth()?.userId;
|
|
52
|
+
const observedAt = Date.now();
|
|
53
|
+
try {
|
|
54
|
+
const account = await accountToken(userId);
|
|
55
|
+
const membership = await fetchProMembership({ ...account, supabaseUrl: SUPABASE_URL, anonKey: SUPABASE_ANON_KEY });
|
|
56
|
+
if (loadAuth()?.userId !== account.userId) throw new Error(changedAccount);
|
|
57
|
+
const result: DesktopMembership = { plan: membership.plan, planExpiresAt: membership.expiresAt ?? null };
|
|
58
|
+
cacheMembership(account.userId, result, observedAt);
|
|
59
|
+
return result;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (userId && loadAuth()?.userId === userId) {
|
|
62
|
+
try { cacheMembership(userId, unknownMembership, observedAt); } catch { /* Preserve the request's error. */ }
|
|
63
|
+
}
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function redeemDesktopPro(code: string, userId: string) {
|
|
69
|
+
if (!code.trim()) throw new Error("请输入激活码");
|
|
70
|
+
if (Buffer.byteLength(code, "utf8") > 512) throw new Error("激活码内容过长,请检查后重试");
|
|
71
|
+
const account = await accountToken(userId);
|
|
72
|
+
const redeemed = await redeemProCode(account.accessToken, code);
|
|
73
|
+
const redemption = { userId, idempotent: redeemed.idempotent, planExpiresAt: redeemed.expiresAt };
|
|
74
|
+
let accountWarning: string | undefined;
|
|
75
|
+
try {
|
|
76
|
+
if (loadAuth()?.userId === userId) cacheMembership(userId, { plan: "pro", planExpiresAt: redeemed.expiresAt });
|
|
77
|
+
} catch {
|
|
78
|
+
accountWarning = "兑换已成功,会员状态暂未保存,请刷新会员状态";
|
|
79
|
+
}
|
|
80
|
+
if (loadAuth()?.userId !== userId) accountWarning = "兑换已成功,登录账号已变更,请切回兑换时的账号查看会员状态";
|
|
81
|
+
return { redemption, accountWarning };
|
|
82
|
+
}
|