@wwkit/opm 1.0.3 → 1.0.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wwkit/opm",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "author": "bluesliu <langcai163@163.com>",
5
5
  "description": "Unified CLI — package management (npm/pip/dnf/apt) + config view + opencode maintenance",
6
6
  "type": "module",
@@ -35,7 +35,7 @@
35
35
  "basic-ftp": "^6.2.1",
36
36
  "extract-zip": "^2.0.1",
37
37
  "systeminformation": "^5.23.0",
38
- "@wwkit/shared": "1.0.6"
38
+ "@wwkit/shared": "1.0.7"
39
39
  },
40
40
  "publishConfig": {
41
41
  "registry": "https://registry.npmjs.org/",
package/src/cli/index.js CHANGED
@@ -22,6 +22,8 @@ import { PingGroup } from './groups/ping.js'
22
22
  import { ProcGroup } from './groups/proc.js'
23
23
  import { FtpGroup } from '../tools/ftp/index.js'
24
24
  import { GitGroup } from '../tools/git/index.js'
25
+ import { ShareGroup } from '../tools/share/index.js'
26
+ import { WebworkGroup } from '../tools/webwork/index.js'
25
27
  import { HarnessGroup } from '../harnesses/base.js'
26
28
  import { opencodeHarness } from '../harnesses/opencode/index.js'
27
29
  import { CftGroup } from '../binaries/cft/index.js'
@@ -43,6 +45,8 @@ const GROUPS = {
43
45
  docker: new DockerGroup(),
44
46
  ftp: new FtpGroup(),
45
47
  git: new GitGroup(),
48
+ share: new ShareGroup(),
49
+ webwork: new WebworkGroup(),
46
50
  composer: new PackageGroup('composer'),
47
51
  bun: new PackageGroup('bun'),
48
52
  winget: new PackageGroup('winget'),
package/src/config.json5 CHANGED
@@ -229,4 +229,22 @@
229
229
  OPENCODE_CONFIG_CONTENT: "",
230
230
  },
231
231
  },
232
+
233
+ // share 文本分享服务
234
+ // server.active 指向预设 key(official/local)或完整 URL
235
+ // serve 为本地服务参数(opm share serve 使用)
236
+ // password 为分享默认密码(opm share upload/get/open 使用,-p/--password 可覆盖)
237
+ share: {
238
+ password: "0000",
239
+ server: {
240
+ official: "http://deepbluenet.com",
241
+ local: "http://127.0.0.1:8787",
242
+ active: "local",
243
+ },
244
+ serve: {
245
+ host: "0.0.0.0",
246
+ port: 8787,
247
+ dataDir: "~/.config/opm/share/data",
248
+ },
249
+ },
232
250
  }
package/src/index.js CHANGED
@@ -23,6 +23,8 @@ export { PingGroup } from './cli/groups/ping.js'
23
23
  export { ProcGroup } from './cli/groups/proc.js'
24
24
  export { FtpGroup } from './tools/ftp/index.js'
25
25
  export { GitGroup } from './tools/git/index.js'
26
+ export { ShareGroup } from './tools/share/index.js'
27
+ export { WebworkGroup } from './tools/webwork/index.js'
26
28
  export { ToolManagerGroup } from './cli/groups/version-manager.js'
27
29
  export { parseFlags } from './cli/helpers/args.js'
28
30
 
@@ -0,0 +1,20 @@
1
+ # opm share server — Apache URL rewrite
2
+ # 将所有 /share/* 请求路由到 server.php
3
+
4
+ <IfModule mod_rewrite.c>
5
+ RewriteEngine On
6
+
7
+ # 如果请求的是已存在的文件,直接返回
8
+ RewriteCond %{REQUEST_FILENAME} -f [OR]
9
+ RewriteCond %{REQUEST_FILENAME} -d
10
+ RewriteRule ^ - [L]
11
+
12
+ # /share/* 路由到 server.php
13
+ RewriteRule ^share/?$ server.php [L,QSA]
14
+ RewriteRule ^share/(.+)$ server.php [L,QSA]
15
+ </IfModule>
16
+
17
+ # 保护 data 目录,禁止直接访问
18
+ <IfModule mod_rewrite.c>
19
+ RewriteRule ^data/ - [F]
20
+ </IfModule>
@@ -0,0 +1,172 @@
1
+ /**
2
+ * share HTTP 客户端 — 上传/取回/列表
3
+ *
4
+ * 通过 fetch 调用 share server API。
5
+ * 服务地址由 config.share.server.active 解析(key → URL,URL 原样返回)。
6
+ */
7
+
8
+ /**
9
+ * 解析 active server URL
10
+ * @param {object} shareConfig - config.share 段
11
+ * @returns {string} base URL(无尾斜杠)
12
+ */
13
+ export function getShareServer(shareConfig) {
14
+ const server = shareConfig?.server ?? {}
15
+ const active = server.active
16
+ if (!active) return server.official || ''
17
+ return server[active] || active
18
+ }
19
+
20
+ /**
21
+ * ping 检测 server 可达性
22
+ * @param {string} baseUrl
23
+ * @returns {Promise<{reachable: boolean, status: number, message: string}>}
24
+ */
25
+ export async function pingServer(baseUrl) {
26
+ try {
27
+ const res = await fetch(`${baseUrl}/share/list?n=1`, {
28
+ signal: AbortSignal.timeout(5000),
29
+ })
30
+ if (res.ok) {
31
+ return { reachable: true, status: res.status, message: 'OK' }
32
+ }
33
+ if (res.status === 404) {
34
+ return { reachable: true, status: res.status, message: 'OK (404 on list endpoint is acceptable)' }
35
+ }
36
+ return { reachable: false, status: res.status, message: `HTTP ${res.status}` }
37
+ } catch (err) {
38
+ return { reachable: false, status: 0, message: err.message }
39
+ }
40
+ }
41
+
42
+ /**
43
+ * 上传分享内容
44
+ * @param {string} baseUrl
45
+ * @param {{title?: string, content: string, password?: string, id?: string, type?: string, filename?: string}} data
46
+ * @returns {Promise<{id: string, url: string, htmlUrl: string}>}
47
+ */
48
+ export async function uploadShare(baseUrl, data) {
49
+ const res = await fetch(`${baseUrl}/share`, {
50
+ method: 'POST',
51
+ headers: { 'Content-Type': 'application/json' },
52
+ body: JSON.stringify(data),
53
+ signal: AbortSignal.timeout(30000),
54
+ })
55
+ if (!res.ok) {
56
+ const body = await res.text()
57
+ throw new Error(`Upload failed: HTTP ${res.status} ${body}`)
58
+ }
59
+ return res.json()
60
+ }
61
+
62
+ /**
63
+ * 取回分享内容
64
+ * @param {string} baseUrl
65
+ * @param {string} id
66
+ * @param {string} [password]
67
+ * @returns {Promise<{id: string, title: string, content: string, created: string}>}
68
+ */
69
+ export async function getShare(baseUrl, id, password) {
70
+ const url = password
71
+ ? `${baseUrl}/share/${id}/${password}`
72
+ : `${baseUrl}/share/${id}`
73
+ const res = await fetch(url, {
74
+ headers: { 'Accept': 'application/json' },
75
+ signal: AbortSignal.timeout(10000),
76
+ })
77
+ if (!res.ok) {
78
+ const body = await res.text()
79
+ throw new Error(`Get failed: HTTP ${res.status} ${body}`)
80
+ }
81
+ return res.json()
82
+ }
83
+
84
+ /**
85
+ * 取回最近一条分享内容
86
+ * @param {string} baseUrl
87
+ * @param {string} [password]
88
+ * @returns {Promise<{id: string, title: string, content: string, created: string}>}
89
+ */
90
+ export async function getLatestShare(baseUrl, password) {
91
+ const url = password
92
+ ? `${baseUrl}/share/latest/${password}`
93
+ : `${baseUrl}/share/latest`
94
+ const res = await fetch(url, {
95
+ headers: { 'Accept': 'application/json' },
96
+ signal: AbortSignal.timeout(10000),
97
+ })
98
+ if (!res.ok) {
99
+ const body = await res.text()
100
+ throw new Error(`Get latest failed: HTTP ${res.status} ${body}`)
101
+ }
102
+ return res.json()
103
+ }
104
+
105
+ /**
106
+ * 获取 HTML 页面 URL
107
+ * @param {string} baseUrl
108
+ * @param {string} id
109
+ * @param {string} [password]
110
+ * @returns {string}
111
+ */
112
+ export function getHtmlUrl(baseUrl, id, password) {
113
+ return password
114
+ ? `${baseUrl}/share/html/${id}/${password}`
115
+ : `${baseUrl}/share/html/${id}`
116
+ }
117
+
118
+ /**
119
+ * 列出最近分享
120
+ * @param {string} baseUrl
121
+ * @param {number} n
122
+ * @returns {Promise<{count: number, items: Array<{id: string, title: string, created: string}>}>}
123
+ */
124
+ export async function listShares(baseUrl, n) {
125
+ const res = await fetch(`${baseUrl}/share/list?n=${n}`, {
126
+ signal: AbortSignal.timeout(10000),
127
+ })
128
+ if (!res.ok) {
129
+ const body = await res.text()
130
+ throw new Error(`List failed: HTTP ${res.status} ${body}`)
131
+ }
132
+ return res.json()
133
+ }
134
+
135
+ /**
136
+ * 删除指定分享
137
+ * @param {string} baseUrl
138
+ * @param {string} id
139
+ * @returns {Promise<{deleted: boolean, id: string}>}
140
+ */
141
+ export async function deleteShare(baseUrl, id) {
142
+ const res = await fetch(`${baseUrl}/share/delete/${id}`, {
143
+ method: 'POST',
144
+ signal: AbortSignal.timeout(10000),
145
+ })
146
+ if (!res.ok) {
147
+ const body = await res.text()
148
+ throw new Error(`Delete failed: HTTP ${res.status} ${body}`)
149
+ }
150
+ return res.json()
151
+ }
152
+
153
+ /**
154
+ * 清理过期/超额分享
155
+ * @param {string} baseUrl
156
+ * @param {{days?: number, keep?: number}} opts
157
+ * @returns {Promise<{deleted: number, ids: string[]}>}
158
+ */
159
+ export async function clearShares(baseUrl, opts) {
160
+ const params = new URLSearchParams()
161
+ if (opts.days) params.set('days', String(opts.days))
162
+ if (opts.keep) params.set('keep', String(opts.keep))
163
+ const res = await fetch(`${baseUrl}/share/clear?${params}`, {
164
+ method: 'POST',
165
+ signal: AbortSignal.timeout(30000),
166
+ })
167
+ if (!res.ok) {
168
+ const body = await res.text()
169
+ throw new Error(`Clear failed: HTTP ${res.status} ${body}`)
170
+ }
171
+ return res.json()
172
+ }