@forsion/extend 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # 更新日志
2
+
3
+ ## 0.1.0 — 2026-09-27
4
+
5
+ - 首个版本。Forsion 云端账号相关的桌面功能首次从 Forsion 本体搬出,作为内置插件随桌面端发布、自动更新:个人中心与会员页入口、额度视图与重置卡、后台额度转换,以及反馈中心提交。
6
+ - 插件以当前账号身份调用 Forsion 云端 API 的通用接口(`cloud:fetch`)也在这个包里,行为与之前一致。
7
+
8
+ First release. The cloud-account features of Forsion Desktop now ship as a built-in plugin that updates on its own: the account center and membership page shortcuts, the quota view and reset cards, background quota conversion, and feedback submission. The shared interface plugins use to call the Forsion cloud API as the signed-in account (`cloud:fetch`) is part of this package too, unchanged in behavior.
package/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ Forsion Extend — Proprietary License
2
+
3
+ Copyright (c) 2026 Forsion. All rights reserved.
4
+
5
+ This software (the "Software": the package @forsion/extend, including its compiled files, manifest, documentation and the SIGNATURE file) is proprietary to Forsion and is licensed, not sold.
6
+
7
+ 1. Permitted use. You may install, run and redistribute the Software only as a component of Forsion, that is: as the built-in bundle that Forsion Desktop (or another Forsion product) seeds, verifies, loads and updates on your behalf, or as an unmodified package obtained from the official distribution channels (the npm registry package @forsion/extend, its mirrors, or a GitHub Release of the Forsion-Extend repository).
8
+
9
+ 2. Restrictions. Except as expressly permitted above, you may not: (a) modify, adapt, translate or create derivative works of the Software; (b) reverse engineer, decompile, disassemble or otherwise attempt to derive the source code of the Software, except to the extent that applicable law expressly permits this despite this restriction; (c) redistribute, sublicense, rent, lease, sell or otherwise make the Software available on its own, outside of Forsion, or under any other name; (d) remove or alter any copyright notice, license text, manifest or signature that ships with the Software; (e) use the Software to build a product that competes with Forsion.
10
+
11
+ 3. Ownership. The Software is protected by copyright and other intellectual property laws. Forsion retains all right, title and interest in and to the Software. No rights are granted other than those expressly stated in this license.
12
+
13
+ 4. Termination. This license terminates automatically if you breach any of its terms. Upon termination you must stop using the Software and destroy all copies in your possession.
14
+
15
+ 5. Disclaimer. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL FORSION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
+
17
+ ---
18
+
19
+ Forsion 扩展 —— 专有授权(中文摘要,以上英文文本为准)
20
+
21
+ 版权所有 (c) 2026 Forsion。保留一切权利。
22
+
23
+ 本软件(@forsion/extend 包,含编译产物、清单、文档与 SIGNATURE 文件)为 Forsion 专有,仅授权使用,不出售。
24
+
25
+ 1. 允许:仅作为 Forsion 的一部分安装、运行与再分发 —— 即由 Forsion 桌面端(或其他 Forsion 产品)代你播种、校验、装载、更新的内置包,或从官方渠道(npm 上的 @forsion/extend 及其镜像、Forsion-Extend 仓库的 GitHub Release)获取的未经修改的包。
26
+ 2. 禁止:修改或制作衍生作品;反编译、反汇编或以其他方式试图还原源代码(法律明文允许的范围除外);脱离 Forsion 单独再分发、转授权、出租或出售;移除或篡改随包的版权声明、授权文本、清单或签名;用本软件构建与 Forsion 竞争的产品。
27
+ 3. 所有权:本软件受著作权及其他知识产权法律保护,全部权利归 Forsion 所有;除本授权明示者外不授予任何权利。
28
+ 4. 终止:违反任一条款即自动终止授权,须停止使用并销毁全部副本。
29
+ 5. 免责:本软件按「现状」提供,不附带任何明示或默示担保。
package/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # Forsion Extend
2
+
3
+ Forsion 桌面端里**跟云端账号打交道的那一半**:个人中心 / 会员页入口、额度视图与重置卡、后台额度转换、反馈中心提交,以及插件以当前账号身份调 Forsion 云端 API 的通用接缝 `cloud:fetch`。它是一个 Forsion 捆绑包(manifest.json + 渲染半身 + 主进程半身),编译产物以 npm 包 `@forsion/extend` 发布。
4
+
5
+ The **cloud-account half of Forsion Desktop**: the account center and membership page shortcuts, the quota view and reset cards, background quota conversion, feedback submission, and `cloud:fetch`, the shared seam plugins use to call the Forsion cloud API as the signed-in account. It is a Forsion bundle (manifest.json + a renderer half + a main-process half), published as the npm package `@forsion/extend`.
6
+
7
+ ## Forsion 桌面端怎么装载它 / How Forsion Desktop loads it
8
+
9
+ Forsion 桌面端把本包当**内置捆绑包**:随桌面安装包带一份,启动时播种进 `<home>/plugins/forsion-extend/`,之后由 npm 更新器在后台下载新版、下次启动换上。带主进程半身的包比普通捆绑包多一道闸:播种 / 换版前,桌面端用钉在自己代码里的 ed25519 公钥核对包内 `SIGNATURE`(每个打包文件的 sha256 + 签名),验不过的那份不换、不载;验过后 `import()` `dist/desktop.mjs`,调用 `registerCloud(host)` 注册 8 个 IPC 通道。
10
+
11
+ Forsion Desktop treats this package as a **built-in bundle**: a copy ships inside the desktop installer, is seeded into `<home>/plugins/forsion-extend/` on startup, and the npm updater downloads newer versions in the background and switches to them on the next launch. A bundle with a main-process half has one extra gate: before seeding or switching, the desktop checks the in-package `SIGNATURE` (sha256 of every packed file plus an ed25519 signature) against the public key pinned in its own code; a copy that fails is neither installed nor loaded. Once verified, the desktop `import()`s `dist/desktop.mjs` and calls `registerCloud(host)`, which registers the 8 IPC channels.
12
+
13
+ | 包内文件 / file | 用途 / role |
14
+ |---|---|
15
+ | `manifest.json` | 捆绑包清单:`id` = `forsion-extend`,`main` = `dist/main.js` / bundle manifest |
16
+ | `dist/main.js` | 渲染半身(iife,宿主 `new Function('ctx', code)` 装载;0.1 为空)/ renderer half, empty in 0.1 |
17
+ | `dist/desktop.mjs` | 主进程半身(ESM,验签后 `import()`)/ main-process half, imported after signature check |
18
+ | `SIGNATURE` | 包内签名 / in-package signature |
19
+ | `CHANGELOG.md` | 显示在桌面「设置 → 插件」详情页 / shown in Settings → Plugins |
20
+
21
+ 这个包不能单独安装或运行:它只在 Forsion 桌面端里作为内置包使用,更新由桌面端自己完成。
22
+
23
+ This package is not installed or run on its own: it is used only as a built-in bundle inside Forsion Desktop, which updates it by itself.
24
+
25
+ ## License
26
+
27
+ 专有授权,见 [LICENSE](./LICENSE):仅允许作为 Forsion 的一部分使用与再分发。Proprietary, see [LICENSE](./LICENSE): use and redistribution only as part of Forsion.
package/SIGNATURE ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "alg": "ed25519",
3
+ "files": {
4
+ "CHANGELOG.md": "c8ddadf7da80157e2bf5126d175f0e6688a6d2bc9963b2c5d464847e1c93ec62",
5
+ "LICENSE": "5594544ee08011ed5785ee26410f1f2ef005f66e9283833e8d435e34c10de1ed",
6
+ "README.md": "36e17262c39554a6db76b696bfa710c276ba3376d7d359783b6724fd4ed5dc62",
7
+ "dist/desktop.mjs": "93845d0cabc393eab8fca4e31bd489366440788eea04d4a8d519a5e3449ca2aa",
8
+ "dist/main.js": "8669f048a0e9a5d47dc657306109ab437da6683ad925fbd6a451aef72542a787",
9
+ "manifest.json": "7cbe478d23b4ad615603076bbcd8a57c7d4c38c297ec4a46e86b0bf74f4996e1",
10
+ "package.json": "d9509d865ec37e06978873df04326b5e2758baa490a778d7c41b8043956d5241"
11
+ },
12
+ "sig": "Z+nk01F3T9Q8No8kjOB48leqxn5uPzLoArPE7GkU6D+9gNusl99iVMkd8NXX7hTf4d2QIo4Tp05Y+oCg6Ca8CQ=="
13
+ }
@@ -0,0 +1 @@
1
+ function m(e,i){if(typeof i!="string"||!i.startsWith("/"))return null;let r=String(e||"").replace(/\/+$/,"").replace(/\/api$/,"");if(!r)return null;let t,n;try{t=new URL(r),n=new URL(r+"/api"+i)}catch{return null}return t.protocol!=="http:"&&t.protocol!=="https:"||n.origin!==t.origin||!n.pathname.startsWith("/api/")?null:n.toString()}async function g(e,i,r,t,n=6e4){let o=await fetch(e.base+r,{method:i,headers:{"Content-Type":"application/json","X-Project-Source":"connect",...e.token?{Authorization:`Bearer ${e.token}`}:{}},body:t===void 0?void 0:JSON.stringify(t),signal:AbortSignal.timeout(n)}),s=await o.json().catch(()=>({}));return{status:o.status,json:s}}function b(e){e.handle("auth:openAccountCenter",async(r,t)=>{let{base:n,token:o}=await e.getCloud();if(!n)return{ok:!1};let s=t&&/^[a-z0-9-]+$/i.test(t)?`#${t}`:"",u=`${n}/account${o?`?token=${encodeURIComponent(o)}`:""}${s}`;return await e.openExternal(u),{ok:!0}}),e.handle("auth:openPayCenter",async()=>{let{base:r,token:t}=await e.getCloud();if(!r)return{ok:!1};let n=`${r}/pay?tab=membership${t?`&token=${encodeURIComponent(t)}`:""}&redirect=${encodeURIComponent(`${r}/account`)}`;return await e.openExternal(n),{ok:!0}});let i=()=>e.getCloud();e.handle("account:quota",async()=>{let r=await i();if(!r.token)return{status:401,json:null};try{return await g(r,"GET","/api/token-quota/my",void 0,15e3)}catch(t){return{status:0,json:{detail:String(t?.message||t)}}}}),e.handle("account:useResetCard",async(r,t)=>{let n=await i();if(!n.token)return{status:401,json:null};if(t!==void 0&&t!=="both"&&t!=="weekly")return{status:400,json:{error:"invalid_type",detail:`invalid reset card type: ${t}`}};let o=t??"both";try{return await g(n,"POST","/api/token-quota/reset-card/use",{type:o},15e3)}catch(s){return{status:0,json:{detail:String(s?.message||s)}}}}),e.handle("account:bgConvert",async(r,t)=>{let n=await i();if(!n.token)return{status:401,json:null};if(!Number.isInteger(t)||t<1||t>100)return{status:400,json:{error:"invalid_percent"}};try{return await g(n,"POST","/api/token-quota/background/convert",{percent:t},15e3)}catch(o){return{status:0,json:{detail:String(o?.message||o)}}}}),e.handle("account:bgAutoMain",async(r,t)=>{let n=await i();if(!n.token)return{status:401,json:null};if(typeof t!="boolean")return{status:400,json:{error:"invalid_request"}};try{return await g(n,"POST","/api/token-quota/background/auto-main",{enabled:t},15e3)}catch(o){return{status:0,json:{detail:String(o?.message||o)}}}}),e.handle("feedback:submit",async(r,t)=>{let{base:n,token:o}=await e.getCloud();if(!n||!o)return{ok:!1,error:"not-logged-in"};let s=(t?.description||"").trim();if(!s)return{ok:!1,error:"empty"};if(s.length>1e4)return{ok:!1,error:"description-too-long"};let u=[],d=!1;if(t?.sessionLogJson){let a=Buffer.from(t.sessionLogJson,"utf8");if(a.length>5*1024*1024)return{ok:!1,error:"attachment-too-large"};u.push({filename:t.sessionLogName||"tangu-session.json",mime_type:"application/json",size:a.length,data_base64:a.toString("base64")})}try{let a=await fetch(`${n}/api/feedback`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${o}`},body:JSON.stringify({description:s,attachments:u}),signal:AbortSignal.timeout(2e4)});if(!a.ok){let c=`HTTP ${a.status}`;try{c=(await a.json())?.detail||c}catch{}return{ok:!1,error:c}}return{ok:!0,id:(await a.json().catch(()=>({})))?.id??null,attachmentSkipped:d}}catch(a){return{ok:!1,error:a?.message||String(a)}}}),e.handle("cloud:fetch",async(r,t)=>{if(!e.isTrustedSender(r))return{status:0,error:"untrusted"};let n=t??{},o=typeof n.path=="string"?n.path:"",s=Math.min(12e4,Math.max(1e3,Number(n.timeoutMs)||15e3)),u=typeof n.method=="string"?n.method.toUpperCase():"GET";if(!/^(GET|POST|PUT|PATCH|DELETE)$/.test(u))return{status:0,error:"bad_method"};if(!o.startsWith("/"))return{status:0,error:"bad_path"};let{base:d,token:a}=await e.getCloud();if(!String(d||""))return{status:0,error:"no_cloud_url"};let f=m(d,o);if(!f)return{status:0,error:"bad_path"};if(!a)return{status:401,error:"not_signed_in"};let c=n.body!==void 0&&u!=="GET"&&u!=="DELETE",p=new AbortController,y=setTimeout(()=>p.abort(),s);try{let l=await fetch(f,{method:u,headers:{Authorization:`Bearer ${a}`,...c?{"Content-Type":"application/json"}:{}},body:c?JSON.stringify(n.body):void 0,signal:p.signal});return{status:l.status,json:await l.json().catch(()=>null)}}catch(l){return{status:0,error:String(l?.message||l)}}finally{clearTimeout(y)}}),e.log("[forsion-extend] cloud account handlers registered")}export{g as cloudJson,b as registerCloud};
package/dist/main.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";(()=>{})();
package/manifest.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "id": "forsion-extend",
3
+ "name": "Forsion 扩展",
4
+ "nameEn": "Forsion Extend",
5
+ "version": "0.1.0",
6
+ "apiVersion": 1,
7
+ "minAppVersion": "2.11.4",
8
+ "main": "dist/main.js",
9
+ "description": "Forsion 云端账号相关的桌面功能:个人中心与会员页入口、额度视图与重置卡、后台额度转换、反馈中心提交,以及插件以当前账号身份调用 Forsion 云端 API 的通用接口。随 Forsion 桌面端内置,自动更新。",
10
+ "descriptionEn": "The cloud-account features of Forsion Desktop: account center and membership page shortcuts, quota view and reset cards, background quota conversion, feedback submission, and the shared interface plugins use to call the Forsion cloud API as the signed-in account. Built into Forsion Desktop and updated automatically.",
11
+ "author": "Forsion"
12
+ }
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@forsion/extend",
3
+ "version": "0.1.0",
4
+ "description": "Forsion Extend: the cloud-account half of Forsion Desktop, shipped as a built-in bundle (signed, updated through npm).",
5
+ "type": "module",
6
+ "license": "SEE LICENSE IN LICENSE",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Changan-Su/Forsion-Extend.git"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "engines": {
15
+ "node": ">=22"
16
+ },
17
+ "files": [
18
+ "manifest.json",
19
+ "dist/main.js",
20
+ "dist/desktop.mjs",
21
+ "SIGNATURE",
22
+ "CHANGELOG.md",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "scripts": {
27
+ "build": "node scripts/build.mjs",
28
+ "sign": "node scripts/sign.mjs",
29
+ "verify": "node scripts/verify-package.mjs",
30
+ "test": "node --test 'test/*.test.ts'",
31
+ "typecheck": "tsc --noEmit"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^22.10.0",
35
+ "esbuild": "^0.25.0",
36
+ "typescript": "^5.6.0"
37
+ }
38
+ }