rssany 0.3.2 → 0.3.4

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.
Files changed (113) hide show
  1. package/.env.example +52 -52
  2. package/README.md +156 -147
  3. package/app/plugins/builtin/email.rssany.js +84 -84
  4. package/app/plugins/builtin/rss.rssany.js +164 -164
  5. package/app/statics/README.md +7 -7
  6. package/app/webui/build/200.html +36 -36
  7. package/app/webui/build/_app/immutable/assets/10.CmGYYZFR.css +1 -0
  8. package/app/webui/build/_app/immutable/assets/11.Dkz3VS_N.css +1 -0
  9. package/app/webui/build/_app/immutable/assets/14.BCCBoMGj.css +1 -0
  10. package/app/webui/build/_app/immutable/assets/6.Cm_jpHOq.css +1 -0
  11. package/app/webui/build/_app/immutable/assets/7.CJ3BjogD.css +1 -0
  12. package/app/webui/build/_app/immutable/assets/9.CATKVZ-n.css +1 -0
  13. package/app/webui/build/_app/immutable/assets/{SourcesList.D5Lso0bo.css → SourcesList.ke66uOSi.css} +1 -1
  14. package/app/webui/build/_app/immutable/assets/chevron-down.CV-KWLNP.css +1 -0
  15. package/app/webui/build/_app/immutable/chunks/{CGCMIfh3.js → 4TuV_psf.js} +1 -1
  16. package/app/webui/build/_app/immutable/chunks/{DAdOEnFb.js → B0czyjwj.js} +1 -1
  17. package/app/webui/build/_app/immutable/chunks/{CFwxUBGi.js → B553hBXT.js} +1 -1
  18. package/app/webui/build/_app/immutable/chunks/B8StT3Do.js +6 -0
  19. package/app/webui/build/_app/immutable/chunks/BI_ale1m.js +1 -0
  20. package/app/webui/build/_app/immutable/chunks/BK0ygNWX.js +2 -0
  21. package/app/webui/build/_app/immutable/chunks/{Brun6sCr.js → BZY5aksi.js} +2 -2
  22. package/app/webui/build/_app/immutable/chunks/{C8umpVpB.js → BnqaikL8.js} +1 -1
  23. package/app/webui/build/_app/immutable/chunks/BsQ08Wq_.js +1 -0
  24. package/app/webui/build/_app/immutable/chunks/C9wTDiHH.js +1 -0
  25. package/app/webui/build/_app/immutable/chunks/{B-CeeY89.js → CAKuIoAf.js} +1 -1
  26. package/app/webui/build/_app/immutable/chunks/CEWi_rGa.js +1 -0
  27. package/app/webui/build/_app/immutable/chunks/CIsxbQ9B.js +1 -0
  28. package/app/webui/build/_app/immutable/chunks/{ChUctqXA.js → Cc7aBSsN.js} +1 -1
  29. package/app/webui/build/_app/immutable/chunks/{DXDBlEGf.js → Czo-EcYU.js} +1 -1
  30. package/app/webui/build/_app/immutable/chunks/{BAJAS8BI.js → D8G961Hm.js} +1 -1
  31. package/app/webui/build/_app/immutable/chunks/{CS53ooo0.js → DIeahUKq.js} +1 -1
  32. package/app/webui/build/_app/immutable/chunks/DO5OXNYS.js +1 -0
  33. package/app/webui/build/_app/immutable/chunks/{Dr67pd7v.js → DhZZHWov.js} +1 -1
  34. package/app/webui/build/_app/immutable/chunks/{Dyvi1wBH.js → DptdhtA1.js} +1 -1
  35. package/app/webui/build/_app/immutable/chunks/{ClknbeNl.js → FDS7fbwH.js} +1 -1
  36. package/app/webui/build/_app/immutable/chunks/{DCEayuDt.js → IhDlsCxD.js} +1 -1
  37. package/app/webui/build/_app/immutable/chunks/Nd0ktDhd.js +1 -0
  38. package/app/webui/build/_app/immutable/chunks/{D6kzEN_P.js → SvdgnirT.js} +1 -1
  39. package/app/webui/build/_app/immutable/chunks/WW6La7Nt.js +2 -0
  40. package/app/webui/build/_app/immutable/chunks/{DsxvjlCC.js → pd_p3yYy.js} +5 -5
  41. package/app/webui/build/_app/immutable/chunks/qwlQuQWi.js +36 -0
  42. package/app/webui/build/_app/immutable/chunks/rNwPv4DZ.js +1 -0
  43. package/app/webui/build/_app/immutable/entry/app.CsaNbZ85.js +2 -0
  44. package/app/webui/build/_app/immutable/entry/start.BZt_oICN.js +1 -0
  45. package/app/webui/build/_app/immutable/nodes/{0.CQDkqUeN.js → 0.zuqEMBQT.js} +3 -3
  46. package/app/webui/build/_app/immutable/nodes/1.DzLFxso0.js +1 -0
  47. package/app/webui/build/_app/immutable/nodes/10.BC-1FXeC.js +1 -0
  48. package/app/webui/build/_app/immutable/nodes/11.CePwEHqu.js +3 -0
  49. package/app/webui/build/_app/immutable/nodes/12.eeQ9B6bg.js +1 -0
  50. package/app/webui/build/_app/immutable/nodes/13.BLKI9iZo.js +1 -0
  51. package/app/webui/build/_app/immutable/nodes/14.T9l5Rh19.js +1 -0
  52. package/app/webui/build/_app/immutable/nodes/15.39GHtAg1.js +1 -0
  53. package/app/webui/build/_app/immutable/nodes/{16.p6WfP435.js → 16.BO-9BBQ5.js} +2 -2
  54. package/app/webui/build/_app/immutable/nodes/17.BWRgFCUw.js +1 -0
  55. package/app/webui/build/_app/immutable/nodes/2.BOYqXdCa.js +1 -0
  56. package/app/webui/build/_app/immutable/nodes/3.CJw5Nx2o.js +1 -0
  57. package/app/webui/build/_app/immutable/nodes/5.9zgwFV6I.js +2 -0
  58. package/app/webui/build/_app/immutable/nodes/6.trFT-trT.js +2 -0
  59. package/app/webui/build/_app/immutable/nodes/7.DCqiDq6O.js +1 -0
  60. package/app/webui/build/_app/immutable/nodes/8.DG_T-wWN.js +1 -0
  61. package/app/webui/build/_app/immutable/nodes/9.FiPTezQy.js +1 -0
  62. package/app/webui/build/_app/version.json +1 -1
  63. package/bin/rssany.js +55 -3
  64. package/dist/index.js +314 -96
  65. package/dist/index.js.map +1 -1
  66. package/package.json +107 -103
  67. package/scripts/postinstall.mjs +44 -0
  68. package/scripts/reset.mjs +137 -135
  69. package/scripts/user-dir.mjs +52 -0
  70. package/app/webui/build/_app/immutable/assets/10.Dj8_pmut.css +0 -1
  71. package/app/webui/build/_app/immutable/assets/13.Qu_tY6H9.css +0 -1
  72. package/app/webui/build/_app/immutable/assets/5.B-dPiwB7.css +0 -1
  73. package/app/webui/build/_app/immutable/assets/6.B27N7pdA.css +0 -1
  74. package/app/webui/build/_app/immutable/assets/8.Cgji2b15.css +0 -1
  75. package/app/webui/build/_app/immutable/assets/9.BsCIAvn3.css +0 -1
  76. package/app/webui/build/_app/immutable/chunks/6prdYIKP.js +0 -1
  77. package/app/webui/build/_app/immutable/chunks/B2cyTHdf.js +0 -2
  78. package/app/webui/build/_app/immutable/chunks/B6WG2Sd3.js +0 -1
  79. package/app/webui/build/_app/immutable/chunks/BA4Gucnq.js +0 -1
  80. package/app/webui/build/_app/immutable/chunks/BXTsojX2.js +0 -36
  81. package/app/webui/build/_app/immutable/chunks/BkD3yAYe.js +0 -1
  82. package/app/webui/build/_app/immutable/chunks/C4uF_YIK.js +0 -1
  83. package/app/webui/build/_app/immutable/chunks/CBY2biv-.js +0 -1
  84. package/app/webui/build/_app/immutable/chunks/DAV9bzjw.js +0 -1
  85. package/app/webui/build/_app/immutable/chunks/DL3Q5sfb.js +0 -1
  86. package/app/webui/build/_app/immutable/chunks/DVa8Y-mQ.js +0 -1
  87. package/app/webui/build/_app/immutable/chunks/DoRPmqLn.js +0 -2
  88. package/app/webui/build/_app/immutable/chunks/vtBo8kBV.js +0 -1
  89. package/app/webui/build/_app/immutable/entry/app.dq7-6soi.js +0 -2
  90. package/app/webui/build/_app/immutable/entry/start.BnoTfBrB.js +0 -1
  91. package/app/webui/build/_app/immutable/nodes/1.BUa24rXB.js +0 -1
  92. package/app/webui/build/_app/immutable/nodes/10.MZgVhpGF.js +0 -1
  93. package/app/webui/build/_app/immutable/nodes/11.B39IbrZ0.js +0 -1
  94. package/app/webui/build/_app/immutable/nodes/12.B5B81dLQ.js +0 -1
  95. package/app/webui/build/_app/immutable/nodes/13.DWWcH27k.js +0 -6
  96. package/app/webui/build/_app/immutable/nodes/14.Db6eOPqq.js +0 -1
  97. package/app/webui/build/_app/immutable/nodes/15.B2jiP2VJ.js +0 -1
  98. package/app/webui/build/_app/immutable/nodes/2.AJd2163d.js +0 -1
  99. package/app/webui/build/_app/immutable/nodes/3.BZQeL-vz.js +0 -1
  100. package/app/webui/build/_app/immutable/nodes/4.BT_N8pCh.js +0 -2
  101. package/app/webui/build/_app/immutable/nodes/5.Bo_ftyqW.js +0 -2
  102. package/app/webui/build/_app/immutable/nodes/6.vOowdQUr.js +0 -1
  103. package/app/webui/build/_app/immutable/nodes/7.BfVbBKZu.js +0 -1
  104. package/app/webui/build/_app/immutable/nodes/8.BslYG5f2.js +0 -1
  105. package/app/webui/build/_app/immutable/nodes/9.DAgT-df2.js +0 -1
  106. /package/app/webui/build/_app/immutable/assets/{11.qYZMiTb0.css → 12.qYZMiTb0.css} +0 -0
  107. /package/app/webui/build/_app/immutable/assets/{12.DfJcfUWl.css → 13.DfJcfUWl.css} +0 -0
  108. /package/app/webui/build/_app/immutable/assets/{14.DfMfOrS3.css → 15.DfMfOrS3.css} +0 -0
  109. /package/app/webui/build/_app/immutable/assets/{15.nNGjXhCQ.css → 17.nNGjXhCQ.css} +0 -0
  110. /package/app/webui/build/_app/immutable/assets/{4.Di6rvlY-.css → 5.Di6rvlY-.css} +0 -0
  111. /package/app/webui/build/_app/immutable/assets/{7.CrNxmd8B.css → 8.CrNxmd8B.css} +0 -0
  112. /package/app/webui/build/_app/immutable/nodes/{17.BtYZF6FM.js → 18.BtYZF6FM.js} +0 -0
  113. /package/app/webui/build/_app/immutable/nodes/{18.BIzqhTqv.js → 4.BIzqhTqv.js} +0 -0
package/package.json CHANGED
@@ -1,103 +1,107 @@
1
- {
2
- "name": "rssany",
3
- "version": "0.3.2",
4
- "description": "Universal RSS/Atom/JSON Feed pipeline — fetches, extracts, parses and converts any web content into consumable feeds with plugin support",
5
- "author": "Joo",
6
- "type": "module",
7
- "main": "./dist/index.js",
8
- "exports": {
9
- ".": "./dist/index.js"
10
- },
11
- "bin": {
12
- "rssany": "./bin/rssany.js"
13
- },
14
- "files": [
15
- "dist",
16
- "bin",
17
- "app/plugins/builtin",
18
- "app/plugins/site.rssany.js",
19
- "statics",
20
- "app/webui/build",
21
- ".env.example",
22
- "README.md",
23
- "init",
24
- "scripts/reset.mjs",
25
- "scripts/dev.mjs"
26
- ],
27
- "engines": {
28
- "node": ">=20 <24"
29
- },
30
- "scripts": {
31
- "build": "vite build",
32
- "dev": "node scripts/dev.mjs",
33
- "dev:backend": "cross-env PORT=3999 tsx app/index.ts",
34
- "start": "node dist/index.js",
35
- "serve:route": "node scripts/serve-route.mjs",
36
- "serve:app": "npx tsx app/index.ts",
37
- "test": "vitest",
38
- "test:run": "vitest run --passWithNoTests",
39
- "lint": "eslint .",
40
- "lint:fix": "eslint . --fix",
41
- "typecheck": "tsc --noEmit",
42
- "reset": "node scripts/reset.mjs",
43
- "proxy-browser": "tsx scripts/proxy-browser.ts",
44
- "webui:install": "cd app/webui && npm install",
45
- "webui:dev": "cd app/webui && npm run build:watch",
46
- "webui:build": "cd app/webui && npm run build",
47
- "webui:watch": "cd app/webui && npm run build:watch",
48
- "dev:all": "npm run dev",
49
- "build:all": "npm run build && npm run webui:build",
50
- "prepublishOnly": "npm run build:all",
51
- "docker:build": "bash scripts/docker-build.sh",
52
- "docker:build:tag": "bash scripts/docker-build.sh",
53
- "landing:install": "cd landing && npm install",
54
- "landing:dev": "cd landing && npm run dev",
55
- "landing:build": "cd landing && npm run build",
56
- "deploy": "node scripts/deploy-landing.mjs",
57
- "deploy:landing": "node scripts/deploy-landing.mjs"
58
- },
59
- "keywords": [
60
- "rss",
61
- "atom",
62
- "json-feed",
63
- "subscription",
64
- "pipeline"
65
- ],
66
- "license": "MIT",
67
- "devDependencies": {
68
- "@eslint/js": "^9.15.0",
69
- "@types/jsdom": "^28.0.3",
70
- "@types/mailparser": "^3.4.6",
71
- "@types/node": "^25.2.0",
72
- "@types/node-cron": "^3.0.11",
73
- "@types/nodemailer": "^7.0.11",
74
- "concurrently": "^9.2.1",
75
- "cross-env": "^7.0.3",
76
- "eslint": "^9.15.0",
77
- "globals": "^15.12.0",
78
- "ssh2": "^1.17.0",
79
- "tsx": "^4.19.0",
80
- "typescript": "~5.6.0",
81
- "typescript-eslint": "^8.15.0",
82
- "vite": "^6.4.3",
83
- "vitest": "^4.1.8"
84
- },
85
- "dependencies": {
86
- "@hono/node-server": "^1.19.10",
87
- "@mozilla/readability": "^0.6.0",
88
- "cron-parser": "^5.0.0",
89
- "dotenv": "^16.4.7",
90
- "hono": "^4.12.12",
91
- "https-proxy-agent": "^7.0.6",
92
- "imapflow": "^1.2.10",
93
- "jsdom": "^29.1.1",
94
- "mailparser": "^3.9.3",
95
- "marked": "^17.0.3",
96
- "node-cron": "^4.2.1",
97
- "node-html-parser": "^7.0.2",
98
- "nodemailer": "^8.0.2",
99
- "openai": "^6.42.0",
100
- "puppeteer-core": "^24.36.0",
101
- "rss-parser": "^3.13.0"
102
- }
103
- }
1
+ {
2
+ "name": "rssany",
3
+ "version": "0.3.4",
4
+ "description": "Universal RSS/Atom/JSON Feed pipeline — fetches, extracts, parses and converts any web content into consumable feeds with plugin support",
5
+ "author": "Joo",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "exports": {
9
+ ".": "./dist/index.js"
10
+ },
11
+ "bin": {
12
+ "rssany": "./bin/rssany.js"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "bin",
17
+ "scripts/user-dir.mjs",
18
+ "app/plugins/builtin",
19
+ "app/plugins/site.rssany.js",
20
+ "statics",
21
+ "app/webui/build",
22
+ ".env.example",
23
+ "README.md",
24
+ "init",
25
+ "scripts/reset.mjs",
26
+ "scripts/postinstall.mjs",
27
+ "scripts/user-dir.mjs",
28
+ "scripts/dev.mjs"
29
+ ],
30
+ "engines": {
31
+ "node": ">=20 <24"
32
+ },
33
+ "scripts": {
34
+ "build": "vite build",
35
+ "dev": "node scripts/dev.mjs",
36
+ "dev:backend": "cross-env PORT=3999 tsx app/index.ts",
37
+ "start": "node dist/index.js",
38
+ "serve:route": "node scripts/serve-route.mjs",
39
+ "serve:app": "npx tsx app/index.ts",
40
+ "test": "vitest",
41
+ "test:run": "vitest run --passWithNoTests",
42
+ "lint": "eslint .",
43
+ "lint:fix": "eslint . --fix",
44
+ "typecheck": "tsc --noEmit",
45
+ "reset": "node scripts/reset.mjs",
46
+ "postinstall": "node scripts/postinstall.mjs",
47
+ "proxy-browser": "tsx scripts/proxy-browser.ts",
48
+ "webui:install": "cd app/webui && npm install",
49
+ "webui:dev": "cd app/webui && npm run build:watch",
50
+ "webui:build": "cd app/webui && npm run build",
51
+ "webui:watch": "cd app/webui && npm run build:watch",
52
+ "dev:all": "npm run dev",
53
+ "build:all": "npm run build && npm run webui:build",
54
+ "prepublishOnly": "npm run build:all",
55
+ "docker:build": "bash scripts/docker-build.sh",
56
+ "docker:build:tag": "bash scripts/docker-build.sh",
57
+ "landing:install": "cd landing && npm install",
58
+ "landing:dev": "cd landing && npm run dev",
59
+ "landing:build": "cd landing && npm run build",
60
+ "deploy": "node scripts/deploy-landing.mjs",
61
+ "deploy:landing": "node scripts/deploy-landing.mjs"
62
+ },
63
+ "keywords": [
64
+ "rss",
65
+ "atom",
66
+ "json-feed",
67
+ "subscription",
68
+ "pipeline"
69
+ ],
70
+ "license": "MIT",
71
+ "devDependencies": {
72
+ "@eslint/js": "^9.15.0",
73
+ "@types/jsdom": "^28.0.3",
74
+ "@types/mailparser": "^3.4.6",
75
+ "@types/node": "^25.2.0",
76
+ "@types/node-cron": "^3.0.11",
77
+ "@types/nodemailer": "^7.0.11",
78
+ "concurrently": "^9.2.1",
79
+ "cross-env": "^7.0.3",
80
+ "eslint": "^9.15.0",
81
+ "globals": "^15.12.0",
82
+ "ssh2": "^1.17.0",
83
+ "tsx": "^4.19.0",
84
+ "typescript": "~5.6.0",
85
+ "typescript-eslint": "^8.15.0",
86
+ "vite": "^6.4.3",
87
+ "vitest": "^4.1.8"
88
+ },
89
+ "dependencies": {
90
+ "@hono/node-server": "^1.19.10",
91
+ "@mozilla/readability": "^0.6.0",
92
+ "cron-parser": "^5.0.0",
93
+ "dotenv": "^16.4.7",
94
+ "hono": "^4.12.12",
95
+ "https-proxy-agent": "^7.0.6",
96
+ "imapflow": "^1.2.10",
97
+ "jsdom": "^29.1.1",
98
+ "mailparser": "^3.9.3",
99
+ "marked": "^17.0.3",
100
+ "node-cron": "^4.2.1",
101
+ "node-html-parser": "^7.0.2",
102
+ "nodemailer": "^8.0.2",
103
+ "openai": "^6.42.0",
104
+ "puppeteer-core": "^24.36.0",
105
+ "rss-parser": "^3.13.0"
106
+ }
107
+ }
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 全局安装后输出用户数据目录位置({npm prefix}/var/rssany)。
4
+ */
5
+
6
+ import { execSync } from "node:child_process";
7
+ import { dirname, join } from "node:path";
8
+ import { fileURLToPath } from "node:url";
9
+ import { isGlobalNpmInstall, resolveDefaultUserDir, resolveNpmPrefixFromPackageRoot } from "./user-dir.mjs";
10
+
11
+ const scriptDir = dirname(fileURLToPath(import.meta.url));
12
+ const packageRoot = join(scriptDir, "..");
13
+
14
+ function getGlobalNpmPrefix() {
15
+ try {
16
+ return execSync("npm prefix -g", { encoding: "utf8", env: process.env }).trim();
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+
22
+ function isSystemOwnedPrefix(prefix) {
23
+ if (!prefix) return false;
24
+ const normalized = prefix.replace(/\\/g, "/");
25
+ return normalized === "/usr/local" || normalized.startsWith("/usr/local/");
26
+ }
27
+
28
+ async function main() {
29
+ if (!isGlobalNpmInstall(packageRoot)) return;
30
+
31
+ const userDir = resolveDefaultUserDir(packageRoot);
32
+ const npmPrefix = resolveNpmPrefixFromPackageRoot(packageRoot) ?? getGlobalNpmPrefix();
33
+ console.log(`[rssany] 用户数据目录: ${userDir}`);
34
+ if (npmPrefix) {
35
+ console.log(`[rssany] npm 全局 prefix: ${npmPrefix}`);
36
+ }
37
+ if (isSystemOwnedPrefix(getGlobalNpmPrefix())) {
38
+ console.log(
39
+ "[rssany] 若此前 npm install -g 报 EACCES,请先执行: npm config set prefix \"$HOME/.local\",并将 \"$HOME/.local/bin\" 加入 PATH",
40
+ );
41
+ }
42
+ }
43
+
44
+ await main();
package/scripts/reset.mjs CHANGED
@@ -1,136 +1,138 @@
1
- #!/usr/bin/env node
2
- /**
3
- * 停止占用 HTTP 服务端口的进程,并删除用户数据目录(与 app 中 PORT / RSSANY_USER_DIR 约定一致)。
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 停止占用 HTTP 服务端口的进程,并删除用户数据目录(与 app 中 PORT / RSSANY_USER_DIR 约定一致)。
4
4
  * 用法:npm run reset 或 PORT=3000 npm run reset
5
- */
6
-
7
- import { execFileSync } from "node:child_process";
8
- import { existsSync, rmSync } from "node:fs";
9
- import { homedir } from "node:os";
10
- import { join } from "node:path";
11
- import { config } from "dotenv";
12
-
13
- config({ path: join(process.cwd(), ".env") });
14
-
15
- const DEFAULT_PORT = 18473;
16
- const port = Number(process.env.PORT) || DEFAULT_PORT;
17
- const userDirRaw = process.env.RSSANY_USER_DIR?.trim();
18
- const userDir =
19
- userDirRaw && userDirRaw.length > 0 ? userDirRaw : join(homedir(), ".rssany");
20
-
21
- function pidsListeningWin32(p) {
22
- const cmd = `Get-NetTCPConnection -LocalPort ${p} -State Listen -ErrorAction SilentlyContinue | ForEach-Object { $_.OwningProcess } | Sort-Object -Unique`;
23
- try {
24
- const out = execFileSync("powershell.exe", ["-NoProfile", "-Command", cmd], {
25
- encoding: "utf8",
26
- windowsHide: true,
27
- });
28
- return [
29
- ...new Set(
30
- out
31
- .trim()
32
- .split(/\r?\n/)
33
- .map((s) => s.trim())
34
- .filter(Boolean)
35
- .map(Number)
36
- .filter((n) => Number.isFinite(n) && n > 0),
37
- ),
38
- ];
39
- } catch {
40
- return [];
41
- }
42
- }
43
-
44
- function pidsListeningNetstatWin32(p) {
45
- const pids = new Set();
46
- try {
47
- const out = execFileSync("netstat", ["-ano"], { encoding: "utf8", windowsHide: true });
48
- const needle = `:${p}`;
49
- for (const line of out.split(/\r?\n/)) {
50
- if (!line.includes("LISTENING") || !line.includes(needle)) continue;
51
- const m = line.trim().match(/LISTENING\s+(\d+)\s*$/);
52
- if (m) pids.add(Number(m[1]));
53
- }
54
- } catch {
55
- // ignore
56
- }
57
- return [...pids];
58
- }
59
-
60
- function pidsListeningUnix(p) {
61
- const argsList = [
62
- ["-nP", `-iTCP:${p}`, "-sTCP:LISTEN", "-t"],
63
- ["-ti", `:${p}`],
64
- ];
65
- for (const args of argsList) {
66
- try {
67
- const out = execFileSync("lsof", args, { encoding: "utf8" });
68
- const pids = out
69
- .trim()
70
- .split(/\n/)
71
- .map((s) => Number(s.trim()))
72
- .filter((n) => Number.isFinite(n) && n > 0);
73
- if (pids.length) return [...new Set(pids)];
74
- } catch {
75
- // try next
76
- }
77
- }
78
- return [];
79
- }
80
-
81
- function killPidsWin32(pids) {
82
- for (const pid of pids) {
83
- if (pid === process.pid) continue;
84
- try {
85
- execFileSync("taskkill", ["/F", "/PID", String(pid)], { stdio: "inherit", windowsHide: true });
86
- console.log(`已结束进程 PID ${pid}`);
87
- } catch {
88
- console.warn(`无法结束进程 PID ${pid}(可能已退出)`);
89
- }
90
- }
91
- }
92
-
93
- function killPidsUnix(pids) {
94
- for (const pid of pids) {
95
- if (pid === process.pid) continue;
96
- try {
97
- process.kill(pid, "SIGTERM");
98
- console.log(`已发送 SIGTERM 至 PID ${pid}`);
99
- } catch {
100
- try {
101
- process.kill(pid, "SIGKILL");
102
- console.log(`已发送 SIGKILL 至 PID ${pid}`);
103
- } catch {
104
- console.warn(`无法结束进程 PID ${pid}`);
105
- }
106
- }
107
- }
108
- }
109
-
110
- function main() {
111
- console.log(`端口: ${port}(来自 PORT 或默认 ${DEFAULT_PORT})`);
112
- console.log(`用户数据目录: ${userDir}`);
113
-
114
- let pids =
115
- process.platform === "win32" ? pidsListeningWin32(port) : pidsListeningUnix(port);
116
- if (process.platform === "win32" && pids.length === 0) {
117
- pids = pidsListeningNetstatWin32(port);
118
- }
119
-
120
- if (pids.length === 0) {
121
- console.log("未发现占用该端口的监听进程。");
122
- } else {
123
- console.log(`将结束占用端口的进程: ${pids.join(", ")}`);
124
- if (process.platform === "win32") killPidsWin32(pids);
125
- else killPidsUnix(pids);
126
- }
127
-
128
- if (!existsSync(userDir)) {
129
- console.log("用户数据目录不存在,跳过删除。");
130
- return;
131
- }
132
- rmSync(userDir, { recursive: true, force: true });
133
- console.log("已删除用户数据目录。");
134
- }
135
-
136
- main();
5
+ */
6
+
7
+ import { execFileSync } from "node:child_process";
8
+ import { existsSync, rmSync } from "node:fs";
9
+ import { join, dirname } from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+ import { config } from "dotenv";
12
+ import { resolveDefaultUserDir } from "./user-dir.mjs";
13
+
14
+ config({ path: join(process.cwd(), ".env") });
15
+
16
+ const scriptDir = dirname(fileURLToPath(import.meta.url));
17
+ const packageRoot = join(scriptDir, "..");
18
+
19
+ const DEFAULT_PORT = 18473;
20
+ const port = Number(process.env.PORT) || DEFAULT_PORT;
21
+ const userDir = resolveDefaultUserDir(packageRoot);
22
+
23
+ function pidsListeningWin32(p) {
24
+ const cmd = `Get-NetTCPConnection -LocalPort ${p} -State Listen -ErrorAction SilentlyContinue | ForEach-Object { $_.OwningProcess } | Sort-Object -Unique`;
25
+ try {
26
+ const out = execFileSync("powershell.exe", ["-NoProfile", "-Command", cmd], {
27
+ encoding: "utf8",
28
+ windowsHide: true,
29
+ });
30
+ return [
31
+ ...new Set(
32
+ out
33
+ .trim()
34
+ .split(/\r?\n/)
35
+ .map((s) => s.trim())
36
+ .filter(Boolean)
37
+ .map(Number)
38
+ .filter((n) => Number.isFinite(n) && n > 0),
39
+ ),
40
+ ];
41
+ } catch {
42
+ return [];
43
+ }
44
+ }
45
+
46
+ function pidsListeningNetstatWin32(p) {
47
+ const pids = new Set();
48
+ try {
49
+ const out = execFileSync("netstat", ["-ano"], { encoding: "utf8", windowsHide: true });
50
+ const needle = `:${p}`;
51
+ for (const line of out.split(/\r?\n/)) {
52
+ if (!line.includes("LISTENING") || !line.includes(needle)) continue;
53
+ const m = line.trim().match(/LISTENING\s+(\d+)\s*$/);
54
+ if (m) pids.add(Number(m[1]));
55
+ }
56
+ } catch {
57
+ // ignore
58
+ }
59
+ return [...pids];
60
+ }
61
+
62
+ function pidsListeningUnix(p) {
63
+ const argsList = [
64
+ ["-nP", `-iTCP:${p}`, "-sTCP:LISTEN", "-t"],
65
+ ["-ti", `:${p}`],
66
+ ];
67
+ for (const args of argsList) {
68
+ try {
69
+ const out = execFileSync("lsof", args, { encoding: "utf8" });
70
+ const pids = out
71
+ .trim()
72
+ .split(/\n/)
73
+ .map((s) => Number(s.trim()))
74
+ .filter((n) => Number.isFinite(n) && n > 0);
75
+ if (pids.length) return [...new Set(pids)];
76
+ } catch {
77
+ // try next
78
+ }
79
+ }
80
+ return [];
81
+ }
82
+
83
+ function killPidsWin32(pids) {
84
+ for (const pid of pids) {
85
+ if (pid === process.pid) continue;
86
+ try {
87
+ execFileSync("taskkill", ["/F", "/PID", String(pid)], { stdio: "inherit", windowsHide: true });
88
+ console.log(`已结束进程 PID ${pid}`);
89
+ } catch {
90
+ console.warn(`无法结束进程 PID ${pid}(可能已退出)`);
91
+ }
92
+ }
93
+ }
94
+
95
+ function killPidsUnix(pids) {
96
+ for (const pid of pids) {
97
+ if (pid === process.pid) continue;
98
+ try {
99
+ process.kill(pid, "SIGTERM");
100
+ console.log(`已发送 SIGTERM 至 PID ${pid}`);
101
+ } catch {
102
+ try {
103
+ process.kill(pid, "SIGKILL");
104
+ console.log(`已发送 SIGKILL 至 PID ${pid}`);
105
+ } catch {
106
+ console.warn(`无法结束进程 PID ${pid}`);
107
+ }
108
+ }
109
+ }
110
+ }
111
+
112
+ function main() {
113
+ console.log(`端口: ${port}(来自 PORT 或默认 ${DEFAULT_PORT})`);
114
+ console.log(`用户数据目录: ${userDir}`);
115
+
116
+ let pids =
117
+ process.platform === "win32" ? pidsListeningWin32(port) : pidsListeningUnix(port);
118
+ if (process.platform === "win32" && pids.length === 0) {
119
+ pids = pidsListeningNetstatWin32(port);
120
+ }
121
+
122
+ if (pids.length === 0) {
123
+ console.log("未发现占用该端口的监听进程。");
124
+ } else {
125
+ console.log(`将结束占用端口的进程: ${pids.join(", ")}`);
126
+ if (process.platform === "win32") killPidsWin32(pids);
127
+ else killPidsUnix(pids);
128
+ }
129
+
130
+ if (!existsSync(userDir)) {
131
+ console.log("用户数据目录不存在,跳过删除。");
132
+ return;
133
+ }
134
+ rmSync(userDir, { recursive: true, force: true });
135
+ console.log("已删除用户数据目录。");
136
+ }
137
+
138
+ main();
@@ -0,0 +1,52 @@
1
+ // 与 app/config/userDir.ts 保持同步(bin/、scripts/ 运行时直接加载)
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+
5
+ const LEGACY_HOME_DIR = ".rssany";
6
+
7
+ export function resolveNpmPrefixFromPackageRoot(packageRoot) {
8
+ const normalized = packageRoot.replace(/\\/g, "/");
9
+ const libSuffix = "/lib/node_modules/rssany";
10
+ if (normalized.endsWith(libSuffix)) {
11
+ return packageRoot.slice(0, packageRoot.length - libSuffix.length);
12
+ }
13
+ const flatSuffix = "/node_modules/rssany";
14
+ if (normalized.endsWith(flatSuffix)) {
15
+ return packageRoot.slice(0, packageRoot.length - flatSuffix.length);
16
+ }
17
+ return null;
18
+ }
19
+
20
+ export function isGlobalNpmInstall(packageRoot) {
21
+ const normalized = packageRoot.replace(/\\/g, "/");
22
+ if (normalized.endsWith("/lib/node_modules/rssany")) return true;
23
+ const globalPatterns = [
24
+ /\/npm\/node_modules\/rssany$/,
25
+ /\/\.local\/lib\/node_modules\/rssany$/,
26
+ /\/\.local\/node_modules\/rssany$/,
27
+ /\/\.npm-global\/lib\/node_modules\/rssany$/,
28
+ /\/\.nvm\/versions\/node\/[^/]+\/lib\/node_modules\/rssany$/,
29
+ /\/\.fnm\/node-versions\/[^/]+\/installation\/lib\/node_modules\/rssany$/,
30
+ ];
31
+ return globalPatterns.some((pattern) => pattern.test(normalized));
32
+ }
33
+
34
+ export function resolveDefaultUserDir(packageRoot) {
35
+ const env = process.env.RSSANY_USER_DIR?.trim();
36
+ if (env) return env;
37
+
38
+ const npmPrefix = resolveNpmPrefixFromPackageRoot(packageRoot);
39
+ if (npmPrefix && isGlobalNpmInstall(packageRoot)) {
40
+ return join(npmPrefix, "var", "rssany");
41
+ }
42
+
43
+ if (!packageRoot.replace(/\\/g, "/").includes("/node_modules/")) {
44
+ return join(packageRoot, ".rssany");
45
+ }
46
+
47
+ return join(homedir(), LEGACY_HOME_DIR);
48
+ }
49
+
50
+ export function getLegacyHomeUserDir() {
51
+ return join(homedir(), LEGACY_HOME_DIR);
52
+ }
@@ -1 +0,0 @@
1
- .feed-wrap.svelte-1f24zj{margin-top:calc(-1 * var(--main-padding-top));width:100%;max-width:42rem}.feed-col.svelte-1f24zj{padding:0}.body.svelte-1f24zj{overflow:visible;padding:1rem 0}.intro.svelte-1f24zj{color:var(--color-muted-foreground-strong);margin:0 0 1.25rem;line-height:1.5;font-size:.875rem}.intro.svelte-1f24zj code:where(.svelte-1f24zj){font-size:.8125rem}.section-title.svelte-1f24zj{font-size:.8125rem;font-weight:600;color:var(--color-muted-foreground);margin:0 0 .5rem}.form-section.svelte-1f24zj{margin-bottom:1.25rem}.text-input.svelte-1f24zj{width:100%;box-sizing:border-box;padding:.5rem .75rem;font-size:.875rem;border:1px solid var(--color-input);border-radius:var(--radius-sm);background:var(--color-card-elevated);color:var(--color-foreground)}.text-input.svelte-1f24zj::placeholder{color:var(--color-muted-foreground-soft)}.hint.svelte-1f24zj{font-size:.75rem;color:var(--color-muted-foreground-soft);margin:.5rem 0 0;line-height:1.45}.btn-row.svelte-1f24zj{display:flex;gap:.75rem}.btn.svelte-1f24zj{padding:.5rem 1rem;font-size:.875rem;border-radius:6px;cursor:pointer;border:none}.btn-primary.svelte-1f24zj{background:var(--color-primary);color:var(--color-primary-foreground)}.btn-primary.svelte-1f24zj:hover:not(:disabled){background:var(--color-primary-hover)}.btn.svelte-1f24zj:disabled{opacity:.6;cursor:not-allowed}@media(max-width:600px){.feed-wrap.svelte-1f24zj{max-width:100%}}
@@ -1 +0,0 @@
1
- [data-select-viewport],[data-combobox-viewport]{scrollbar-width:none!important;-ms-overflow-style:none!important;-webkit-overflow-scrolling:touch!important}[data-combobox-viewport]::-webkit-scrollbar{display:none!important}[data-select-viewport]::-webkit-scrollbar{display:none!important}.feed-wrap.svelte-1lsf4ps{--feed-sticky-gap-after: 0;margin-top:calc(-1 * var(--main-padding-top));width:100%;max-width:100%;display:flex;flex-direction:column;flex:1;min-height:0;overflow:hidden}.feed-col.svelte-1lsf4ps{flex:1;display:flex;flex-direction:column;overflow:hidden;min-height:0;background:transparent}.feed-toolbar-block.svelte-1lsf4ps{flex-shrink:0;padding-top:var(--main-padding-top);padding-bottom:var(--feed-sticky-gap-after)}.feed-toolbar-block.svelte-1lsf4ps .admin-feed-header{min-height:4.1rem}.log-filter-input.svelte-1lsf4ps{width:min(16rem,34vw)}.log-level-trigger.svelte-1lsf4ps{width:7rem;min-width:7rem;height:2rem;min-height:2rem;box-sizing:border-box;display:inline-flex;align-items:center;justify-content:space-between;gap:.4rem;padding:.35rem .55rem .35rem .6rem;font-size:.8125rem;font-family:inherit;color:var(--color-foreground);background:var(--color-card-elevated);border:1px solid var(--color-input);border-radius:var(--radius-sm);flex-shrink:0;cursor:pointer;font-variant-numeric:tabular-nums}.log-level-trigger.svelte-1lsf4ps:hover{background:var(--color-card);border-color:var(--color-border)}.log-level-trigger.svelte-1lsf4ps:focus-visible{outline:none;border-color:var(--color-primary)}.log-level-trigger.svelte-1lsf4ps svg{flex-shrink:0;color:var(--color-muted-foreground)}.log-level-content{z-index:120;min-width:7rem;padding:.25rem;background:var(--color-card-elevated);border:1px solid var(--color-border);border-radius:var(--radius-md);box-shadow:var(--shadow-panel)}.log-level-viewport{display:flex;flex-direction:column;gap:.05rem}.log-level-item{display:flex;align-items:center;min-height:1.85rem;padding:.35rem .55rem;font-size:.8125rem;line-height:1.25;color:var(--color-foreground);border-radius:var(--radius-sm);outline:none;cursor:pointer;font-variant-numeric:tabular-nums}.log-level-item[data-highlighted]{background:var(--color-muted)}.log-level-item[data-selected]{color:var(--color-primary);background:var(--color-primary-light)}.log-body-scroll.svelte-1lsf4ps,.log-has-data.svelte-1lsf4ps{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.log-table-scroll.svelte-1lsf4ps{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;overscroll-behavior-y:contain;-webkit-overflow-scrolling:touch}.log-table-scroll.svelte-1lsf4ps::-webkit-scrollbar{width:4px}.log-table-scroll.svelte-1lsf4ps::-webkit-scrollbar-thumb{background:var(--color-scrollbar-thumb);border-radius:2px}.btn.svelte-1lsf4ps{display:inline-flex;align-items:center;padding:.4rem .9rem;border:none;border-radius:var(--radius-sm);cursor:pointer;font-size:.875rem;font-family:inherit;transition:background .15s}.btn-primary.svelte-1lsf4ps{background:var(--color-primary);color:var(--color-primary-foreground)}.btn-primary.svelte-1lsf4ps:hover:not(:disabled){background:var(--color-primary-hover)}.btn-secondary.svelte-1lsf4ps{background:var(--color-muted);color:var(--color-foreground);border:1px solid var(--color-border)}.btn-secondary.svelte-1lsf4ps:hover:not(:disabled){background:var(--color-accent)}.btn.svelte-1lsf4ps:disabled{opacity:.4;cursor:not-allowed}.btn-sm.svelte-1lsf4ps{padding:.25rem .5rem;font-size:.75rem}.table-wrap.svelte-1lsf4ps{width:100%}table.svelte-1lsf4ps{width:100%;table-layout:fixed;border-collapse:collapse}thead.svelte-1lsf4ps th:where(.svelte-1lsf4ps){position:sticky;top:0;z-index:3;background:var(--color-card-elevated);font-size:.72rem;font-weight:600;color:var(--color-muted-foreground);text-transform:uppercase;letter-spacing:.04em;padding:.5rem .75rem;text-align:left;border-bottom:1px solid var(--color-border);box-shadow:0 1px 0 var(--color-border)}tbody.svelte-1lsf4ps tr:where(.svelte-1lsf4ps){border-bottom:1px solid var(--color-border-muted)}tbody.svelte-1lsf4ps tr:where(.svelte-1lsf4ps):hover{background:var(--color-muted)}tbody.svelte-1lsf4ps td:where(.svelte-1lsf4ps){padding:.5rem .75rem;font-size:.8125rem;vertical-align:middle}.th-time.svelte-1lsf4ps{width:130px}.th-level.svelte-1lsf4ps{width:68px}.th-cat.svelte-1lsf4ps{width:7rem;max-width:28vw}.th-msg.svelte-1lsf4ps{min-width:0}.log-row.svelte-1lsf4ps{cursor:pointer}.log-row.svelte-1lsf4ps:focus{outline:1px dotted var(--color-muted-foreground);outline-offset:-1px}.td-time.svelte-1lsf4ps{color:var(--color-muted-foreground);white-space:nowrap}.td-level.svelte-1lsf4ps{white-space:nowrap}.badge.svelte-1lsf4ps{display:inline-block;padding:.12rem .4rem;border-radius:4px;font-size:.7rem;font-weight:500}.badge.level-error.svelte-1lsf4ps{background:color-mix(in srgb,var(--color-destructive) 18%,transparent);color:var(--color-destructive);border:1px solid color-mix(in srgb,var(--color-destructive) 45%,transparent)}.badge.level-warn.svelte-1lsf4ps{background:#eab30824;color:#fbbf24;border:1px solid rgba(234,179,8,.35)}.badge.level-info.svelte-1lsf4ps{background:var(--color-primary-light);color:var(--color-primary-hover);border:1px solid color-mix(in srgb,var(--color-primary) 40%,transparent)}.badge.level-debug.svelte-1lsf4ps{background:var(--color-muted);color:var(--color-muted-foreground-strong);border:1px solid var(--color-border)}.td-cat.svelte-1lsf4ps{color:var(--color-muted-foreground-strong)}.td-msg.svelte-1lsf4ps{word-break:break-word;max-width:320px}.payload-row.svelte-1lsf4ps td:where(.svelte-1lsf4ps){padding:.25rem .75rem .65rem;background:transparent;border-bottom:1px solid var(--color-border-muted);vertical-align:top}.payload-content.svelte-1lsf4ps{margin:0;padding:0;font-family:Menlo,Monaco,Consolas,monospace;font-size:.72rem;line-height:1.5;background:transparent;border:none;border-radius:0;overflow-x:auto;max-height:200px;overflow-y:auto;white-space:pre-wrap;word-break:break-all;color:var(--color-muted-foreground-strong)}.log-toolbar.svelte-1lsf4ps{display:flex;align-items:center;justify-content:space-between;padding:.65rem 0 .85rem;flex-shrink:0}.pagination-info.svelte-1lsf4ps{font-size:.8125rem;color:var(--color-muted-foreground)}.pagination-btns.svelte-1lsf4ps{display:flex;gap:.5rem}.state.svelte-1lsf4ps{flex:1;display:flex;align-items:center;justify-content:center;text-align:center;padding:3rem 1.5rem;color:var(--color-muted-foreground-soft);font-size:.875rem}.state.err.svelte-1lsf4ps{flex:1;align-self:stretch;color:var(--color-destructive);background:color-mix(in srgb,var(--color-destructive) 12%,transparent);border-radius:var(--radius-md);padding:1rem;margin:.75rem 0}@media(max-width:720px){.feed-wrap.svelte-1lsf4ps{max-width:100%}.payload-row.svelte-1lsf4ps td:where(.svelte-1lsf4ps){padding-left:.5rem}.payload-row.svelte-1lsf4ps td[colspan="4"]:where(.svelte-1lsf4ps){padding-left:.5rem}}
@@ -1 +0,0 @@
1
- .feed-wrap.svelte-bhaf7m{margin-top:calc(-1 * var(--main-padding-top));width:100%;max-width:42rem}.feed-col.svelte-bhaf7m{padding:0}.body.svelte-bhaf7m{overflow:visible;padding:1rem 0}.intro.svelte-bhaf7m{color:var(--color-muted-foreground-strong);margin:0 0 1.25rem;line-height:1.5;font-size:.875rem}.intro.svelte-bhaf7m code:where(.svelte-bhaf7m){font-size:.8125rem}.section-title.svelte-bhaf7m{font-size:.8125rem;font-weight:600;color:var(--color-muted-foreground);margin:0 0 .5rem}.form-section.svelte-bhaf7m{margin-bottom:1.25rem}.url-input.svelte-bhaf7m{width:100%;box-sizing:border-box;padding:.5rem .75rem;font-size:.875rem;border:1px solid var(--color-input);border-radius:var(--radius-sm);background:var(--color-card-elevated);color:var(--color-foreground)}.url-input.svelte-bhaf7m::placeholder{color:var(--color-muted-foreground-soft)}.hint.svelte-bhaf7m{font-size:.75rem;color:var(--color-muted-foreground-soft);margin:.5rem 0 0;line-height:1.45}.btn-row.svelte-bhaf7m{display:flex;gap:.75rem;flex-wrap:wrap}.btn.svelte-bhaf7m{padding:.5rem 1rem;font-size:.875rem;border-radius:6px;cursor:pointer;border:none}.btn-primary.svelte-bhaf7m{background:var(--color-primary);color:var(--color-primary-foreground)}.btn-primary.svelte-bhaf7m:hover:not(:disabled){background:var(--color-primary-hover)}.btn-secondary.svelte-bhaf7m{background:var(--color-muted);color:var(--color-foreground);border:1px solid var(--color-border)}.btn-secondary.svelte-bhaf7m:hover:not(:disabled){background:var(--color-accent)}.btn.svelte-bhaf7m:disabled{opacity:.6;cursor:not-allowed}@media(max-width:600px){.feed-wrap.svelte-bhaf7m{max-width:100%}}
@@ -1 +0,0 @@
1
- .feed-wrap.svelte-1a0dvmg{margin-top:calc(-1 * var(--main-padding-top));width:100%;max-width:42rem}.feed-col.svelte-1a0dvmg{padding:0}.body.svelte-1a0dvmg{overflow:visible;padding:1rem 0}.intro.svelte-1a0dvmg{color:var(--color-muted-foreground-strong);margin:0 0 1.25rem;line-height:1.5;font-size:.875rem}.section-title.svelte-1a0dvmg{font-size:.8125rem;font-weight:600;color:var(--color-muted-foreground);margin:0 0 .5rem}.form-section.svelte-1a0dvmg{margin-bottom:1.25rem}.text-input.svelte-1a0dvmg{width:100%;box-sizing:border-box;padding:.5rem .75rem;font-size:.875rem;border:1px solid var(--color-input);border-radius:var(--radius-sm);background:var(--color-card-elevated);color:var(--color-foreground)}.text-input.svelte-1a0dvmg::placeholder{color:var(--color-muted-foreground-soft)}.hint.svelte-1a0dvmg{font-size:.75rem;color:var(--color-muted-foreground-soft);margin:.5rem 0 0;line-height:1.45}.btn-row.svelte-1a0dvmg{display:flex;gap:.75rem}.btn.svelte-1a0dvmg{padding:.5rem 1rem;font-size:.875rem;border-radius:6px;cursor:pointer;border:none}.btn-primary.svelte-1a0dvmg{background:var(--color-primary);color:var(--color-primary-foreground)}.btn-primary.svelte-1a0dvmg:hover:not(:disabled){background:var(--color-primary-hover)}.btn-secondary.svelte-1a0dvmg{background:var(--color-muted);color:var(--color-foreground);border:1px solid var(--color-border)}.btn-secondary.svelte-1a0dvmg:hover:not(:disabled){background:var(--color-accent)}.btn.svelte-1a0dvmg:disabled{opacity:.6;cursor:not-allowed}@media(max-width:600px){.feed-wrap.svelte-1a0dvmg{max-width:100%}}
@@ -1 +0,0 @@
1
- .feed-wrap.svelte-1x680s0{margin-top:calc(-1 * var(--main-padding-top));width:100%;max-width:42rem}.feed-col.svelte-1x680s0{padding:0}.feed-header.svelte-1x680s0{padding:.875rem 0;flex-shrink:0;border-bottom:1px solid var(--color-border-muted)}.feed-header.svelte-1x680s0 h2:where(.svelte-1x680s0){font-size:.9375rem;font-weight:600;margin:0 0 .25rem;color:var(--color-foreground)}.page-desc.svelte-1x680s0{font-size:.75rem;color:var(--color-muted-foreground-soft);margin:0;line-height:1.5}.body.svelte-1x680s0{flex:1;overflow:visible;padding:1rem 0}.url-row.svelte-1x680s0{display:flex;gap:.5rem;margin-bottom:.75rem}.url-row.svelte-1x680s0 input:where(.svelte-1x680s0){flex:1;padding:.65rem 1rem;border:1px solid var(--color-input);border-radius:var(--radius-md);font-size:.9375rem;outline:none;transition:border .15s,box-shadow .15s;min-width:0;background:var(--color-card-elevated);color:var(--color-foreground);font-family:inherit}.url-row.svelte-1x680s0 input:where(.svelte-1x680s0):focus{border-color:var(--color-primary);box-shadow:0 0 0 3px var(--color-primary-light)}.url-row.svelte-1x680s0 button:where(.svelte-1x680s0){padding:.65rem 1.375rem;background:var(--color-primary);color:var(--color-primary-foreground);border:none;border-radius:8px;cursor:pointer;font-size:.9rem;font-family:inherit;white-space:nowrap;transition:background .15s}.url-row.svelte-1x680s0 button:where(.svelte-1x680s0):hover:not(:disabled){background:var(--color-primary-hover)}.url-row.svelte-1x680s0 button:where(.svelte-1x680s0):disabled{opacity:.65;cursor:not-allowed}.result-error.svelte-1x680s0{margin-top:1rem;width:100%;padding:.75rem 1rem;border-radius:var(--radius-md);background:color-mix(in srgb,var(--color-destructive, #c00) 12%,transparent);color:var(--color-foreground);font-size:.8125rem;white-space:pre-wrap;word-break:break-word}.result-wrap.svelte-1x680s0{margin-top:1rem;width:100%}.result-label.svelte-1x680s0{font-size:.75rem;font-weight:600;color:var(--color-muted-foreground);margin:0 0 .4rem}.result-json.svelte-1x680s0{margin:0;padding:1rem;max-height:min(70vh,520px);overflow:auto;font-size:.75rem;line-height:1.45;border-radius:var(--radius-md);border:1px solid var(--color-input);background:var(--color-muted);color:var(--color-foreground)}.proxy-row.svelte-1x680s0{margin-bottom:.75rem}.proxy-row.svelte-1x680s0 input:where(.svelte-1x680s0){width:100%;box-sizing:border-box;padding:.55rem 1rem;border:1px solid var(--color-input);border-radius:var(--radius-md);font-size:.8125rem;outline:none;transition:border .15s,box-shadow .15s;background:var(--color-card-elevated);color:var(--color-foreground);font-family:ui-monospace,monospace}.proxy-row.svelte-1x680s0 input:where(.svelte-1x680s0):focus{border-color:var(--color-primary);box-shadow:0 0 0 3px var(--color-primary-light)}.info-box.svelte-1x680s0{margin-top:2rem;width:100%;background:var(--color-primary-light);border:1px solid color-mix(in srgb,var(--color-primary) 38%,transparent);border-radius:var(--radius-md);padding:.875rem 1.125rem;font-size:.8rem;color:var(--color-muted-foreground-strong);line-height:1.7}.info-box.svelte-1x680s0 p:where(.svelte-1x680s0){margin:0 0 .65rem}.info-box.svelte-1x680s0 p:where(.svelte-1x680s0):last-child{margin-bottom:0}.info-note.svelte-1x680s0{color:var(--color-muted-foreground);font-size:.78rem}.info-box.svelte-1x680s0 code:where(.svelte-1x680s0){background:var(--color-muted);padding:.1rem .35rem;border-radius:3px;font-family:monospace}@media(max-width:600px){.feed-wrap.svelte-1x680s0{max-width:100%}}