dsh-plugin-image-workbench 1.1.5 → 1.1.7
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/LICENSE +21 -21
- package/README.en.md +5 -3
- package/README.md +7 -3
- package/client.js +241 -241
- package/cordis.patch.yml +14 -14
- package/host.js +51 -0
- package/install.mjs +333 -387
- package/package.json +3 -2
- package/scripts/fetch-style-images.mjs +104 -0
- package/scripts/vendor-snapshot.py +154 -154
- package/server/access.py +26 -26
- package/server/app.py +1371 -1329
- package/server/catalog.py +147 -147
- package/server/composer.py +391 -391
- package/server/drafts.py +158 -158
- package/server/history.py +575 -575
- package/server/imaging.py +172 -172
- package/server/pool.py +1335 -1335
- package/server/pool_admin.py +921 -921
- package/server/pool_service.py +1073 -1073
- package/server/requirements.txt +6 -6
- package/server/smoke_check.py +402 -402
- package/server/static/access.html +202 -202
- package/server/static/index.html +15 -0
- package/vendor/chatgpt2api/source/.env.example +82 -82
- package/vendor/chatgpt2api/source/.python-version +1 -1
- package/vendor/chatgpt2api/source/LICENSE +21 -21
- package/vendor/chatgpt2api/source/README.md +391 -391
- package/vendor/chatgpt2api/source/VERSION +1 -1
- package/vendor/chatgpt2api/source/api/__init__.py +2 -2
- package/vendor/chatgpt2api/source/api/accounts.py +537 -537
- package/vendor/chatgpt2api/source/api/ai.py +217 -217
- package/vendor/chatgpt2api/source/api/app.py +62 -62
- package/vendor/chatgpt2api/source/api/errors.py +46 -46
- package/vendor/chatgpt2api/source/api/image_inputs.py +309 -309
- package/vendor/chatgpt2api/source/api/image_tasks.py +120 -120
- package/vendor/chatgpt2api/source/api/support.py +134 -134
- package/vendor/chatgpt2api/source/api/system.py +365 -365
- package/vendor/chatgpt2api/source/assets/kunrouai.svg +50 -50
- package/vendor/chatgpt2api/source/config.default.json +108 -108
- package/vendor/chatgpt2api/source/main.py +9 -9
- package/vendor/chatgpt2api/source/pyproject.toml +27 -27
- package/vendor/chatgpt2api/source/scripts/init_proxy_config.py +186 -186
- package/vendor/chatgpt2api/source/scripts/migrate_storage.py +163 -163
- package/vendor/chatgpt2api/source/scripts/privoxy-warp.conf +13 -13
- package/vendor/chatgpt2api/source/scripts/test_outlook_token_mailbox.py +385 -385
- package/vendor/chatgpt2api/source/scripts/test_storage.py +129 -129
- package/vendor/chatgpt2api/source/scripts/verify_oauth_refresh.py +92 -92
- package/vendor/chatgpt2api/source/services/account_service.py +1757 -1757
- package/vendor/chatgpt2api/source/services/auth_service.py +253 -253
- package/vendor/chatgpt2api/source/services/backup_service.py +671 -671
- package/vendor/chatgpt2api/source/services/config.py +667 -667
- package/vendor/chatgpt2api/source/services/content_filter.py +242 -242
- package/vendor/chatgpt2api/source/services/cpa_service.py +315 -315
- package/vendor/chatgpt2api/source/services/editable_file_task_service.py +260 -260
- package/vendor/chatgpt2api/source/services/image_service.py +376 -376
- package/vendor/chatgpt2api/source/services/image_storage_service.py +405 -405
- package/vendor/chatgpt2api/source/services/image_tags_service.py +76 -76
- package/vendor/chatgpt2api/source/services/image_task_service.py +550 -550
- package/vendor/chatgpt2api/source/services/log_service.py +337 -337
- package/vendor/chatgpt2api/source/services/model_service.py +182 -182
- package/vendor/chatgpt2api/source/services/oauth_login_service.py +267 -267
- package/vendor/chatgpt2api/source/services/openai_backend_api.py +2763 -2763
- package/vendor/chatgpt2api/source/services/openai_oauth.py +32 -32
- package/vendor/chatgpt2api/source/services/protocol/__init__.py +2 -2
- package/vendor/chatgpt2api/source/services/protocol/anthropic_v1_messages.py +307 -307
- package/vendor/chatgpt2api/source/services/protocol/chat_completion_cache.py +238 -238
- package/vendor/chatgpt2api/source/services/protocol/conversation.py +1644 -1644
- package/vendor/chatgpt2api/source/services/protocol/error_response.py +124 -124
- package/vendor/chatgpt2api/source/services/protocol/openai_search.py +19 -19
- package/vendor/chatgpt2api/source/services/protocol/openai_v1_chat_complete.py +316 -316
- package/vendor/chatgpt2api/source/services/protocol/openai_v1_image_edit.py +86 -86
- package/vendor/chatgpt2api/source/services/protocol/openai_v1_image_generations.py +42 -42
- package/vendor/chatgpt2api/source/services/protocol/openai_v1_models.py +53 -53
- package/vendor/chatgpt2api/source/services/protocol/openai_v1_response.py +453 -453
- package/vendor/chatgpt2api/source/services/protocol/web_search_tool.py +160 -160
- package/vendor/chatgpt2api/source/services/proxy_service.py +648 -648
- package/vendor/chatgpt2api/source/services/storage/__init__.py +5 -5
- package/vendor/chatgpt2api/source/services/storage/base.py +38 -38
- package/vendor/chatgpt2api/source/services/storage/database_storage.py +194 -194
- package/vendor/chatgpt2api/source/services/storage/factory.py +104 -104
- package/vendor/chatgpt2api/source/services/storage/git_storage.py +183 -183
- package/vendor/chatgpt2api/source/services/storage/json_storage.py +95 -95
- package/vendor/chatgpt2api/source/services/sub2api_service.py +531 -531
- package/vendor/chatgpt2api/source/utils/helper.py +469 -469
- package/vendor/chatgpt2api/source/utils/image_tokens.py +346 -346
- package/vendor/chatgpt2api/source/utils/log.py +110 -110
- package/vendor/chatgpt2api/source/utils/pkce.py +19 -19
- package/vendor/chatgpt2api/source/utils/pow.py +207 -207
- package/vendor/chatgpt2api/source/utils/sentinel.py +159 -159
- package/vendor/chatgpt2api/source/utils/turnstile.py +184 -184
- package/vendor/chatgpt2api/source/uv.lock +904 -904
- package/vendor/chatgpt2api/source/web/bun.lock +1501 -1501
- package/vendor/chatgpt2api/source/web/components.json +24 -24
- package/vendor/chatgpt2api/source/web/eslint.config.mjs +17 -17
- package/vendor/chatgpt2api/source/web/next.config.ts +40 -40
- package/vendor/chatgpt2api/source/web/package.json +58 -58
- package/vendor/chatgpt2api/source/web/postcss.config.mjs +5 -5
- package/vendor/chatgpt2api/source/web/src/app/accounts/components/account-import-dialog.tsx +925 -925
- package/vendor/chatgpt2api/source/web/src/app/accounts/page.tsx +1307 -1307
- package/vendor/chatgpt2api/source/web/src/app/debug/components/chat-panel.tsx +224 -224
- package/vendor/chatgpt2api/source/web/src/app/debug/components/editable-file-panel.tsx +444 -444
- package/vendor/chatgpt2api/source/web/src/app/debug/components/ppt-panel.tsx +9 -9
- package/vendor/chatgpt2api/source/web/src/app/debug/components/psd-panel.tsx +9 -9
- package/vendor/chatgpt2api/source/web/src/app/debug/components/search-panel.tsx +172 -172
- package/vendor/chatgpt2api/source/web/src/app/debug/components/skill-panel.tsx +169 -169
- package/vendor/chatgpt2api/source/web/src/app/debug/components/types.ts +39 -39
- package/vendor/chatgpt2api/source/web/src/app/debug/page.tsx +59 -59
- package/vendor/chatgpt2api/source/web/src/app/globals.css +230 -230
- package/vendor/chatgpt2api/source/web/src/app/image/components/image-composer.tsx +542 -542
- package/vendor/chatgpt2api/source/web/src/app/image/components/image-results.tsx +554 -554
- package/vendor/chatgpt2api/source/web/src/app/image/components/image-sidebar.tsx +176 -176
- package/vendor/chatgpt2api/source/web/src/app/image/page.tsx +1799 -1799
- package/vendor/chatgpt2api/source/web/src/app/image-manager/page.tsx +732 -732
- package/vendor/chatgpt2api/source/web/src/app/layout.tsx +50 -50
- package/vendor/chatgpt2api/source/web/src/app/login/page.tsx +101 -101
- package/vendor/chatgpt2api/source/web/src/app/logs/page.tsx +356 -356
- package/vendor/chatgpt2api/source/web/src/app/page.tsx +30 -30
- package/vendor/chatgpt2api/source/web/src/app/settings/components/api-docs-card.tsx +341 -341
- package/vendor/chatgpt2api/source/web/src/app/settings/components/backup-settings-card.tsx +447 -447
- package/vendor/chatgpt2api/source/web/src/app/settings/components/base-url-card.tsx +71 -71
- package/vendor/chatgpt2api/source/web/src/app/settings/components/config-card.tsx +491 -491
- package/vendor/chatgpt2api/source/web/src/app/settings/components/cpa-pool-dialog.tsx +108 -108
- package/vendor/chatgpt2api/source/web/src/app/settings/components/cpa-pools-card.tsx +169 -169
- package/vendor/chatgpt2api/source/web/src/app/settings/components/import-browser-dialog.tsx +185 -185
- package/vendor/chatgpt2api/source/web/src/app/settings/components/proxy-runtime-card.tsx +379 -379
- package/vendor/chatgpt2api/source/web/src/app/settings/components/proxy-settings-card.tsx +127 -127
- package/vendor/chatgpt2api/source/web/src/app/settings/components/proxy-settings.tsx +200 -200
- package/vendor/chatgpt2api/source/web/src/app/settings/components/settings-header.tsx +12 -12
- package/vendor/chatgpt2api/source/web/src/app/settings/components/sub2api-connections.tsx +886 -886
- package/vendor/chatgpt2api/source/web/src/app/settings/components/third-party-apps-card.tsx +81 -81
- package/vendor/chatgpt2api/source/web/src/app/settings/components/user-keys-card.tsx +422 -422
- package/vendor/chatgpt2api/source/web/src/app/settings/page.tsx +135 -135
- package/vendor/chatgpt2api/source/web/src/app/settings/store.ts +1069 -1069
- package/vendor/chatgpt2api/source/web/src/components/date-range-filter.tsx +49 -49
- package/vendor/chatgpt2api/source/web/src/components/header-actions.tsx +24 -24
- package/vendor/chatgpt2api/source/web/src/components/image-lightbox.tsx +434 -434
- package/vendor/chatgpt2api/source/web/src/components/image-thumbnail.tsx +46 -46
- package/vendor/chatgpt2api/source/web/src/components/theme-script.tsx +18 -18
- package/vendor/chatgpt2api/source/web/src/components/theme-toggle.tsx +14 -14
- package/vendor/chatgpt2api/source/web/src/components/top-nav.tsx +263 -263
- package/vendor/chatgpt2api/source/web/src/components/ui/animated-theme-toggler.tsx +256 -256
- package/vendor/chatgpt2api/source/web/src/components/ui/badge.tsx +41 -41
- package/vendor/chatgpt2api/source/web/src/components/ui/button.tsx +59 -59
- package/vendor/chatgpt2api/source/web/src/components/ui/calendar.tsx +46 -46
- package/vendor/chatgpt2api/source/web/src/components/ui/card.tsx +61 -61
- package/vendor/chatgpt2api/source/web/src/components/ui/checkbox.tsx +27 -27
- package/vendor/chatgpt2api/source/web/src/components/ui/dia-text-reveal.tsx +267 -267
- package/vendor/chatgpt2api/source/web/src/components/ui/dialog.tsx +132 -132
- package/vendor/chatgpt2api/source/web/src/components/ui/field.tsx +13 -13
- package/vendor/chatgpt2api/source/web/src/components/ui/input.tsx +19 -19
- package/vendor/chatgpt2api/source/web/src/components/ui/label.tsx +24 -24
- package/vendor/chatgpt2api/source/web/src/components/ui/morphing-text.tsx +149 -149
- package/vendor/chatgpt2api/source/web/src/components/ui/popover.tsx +38 -38
- package/vendor/chatgpt2api/source/web/src/components/ui/select.tsx +175 -175
- package/vendor/chatgpt2api/source/web/src/components/ui/sheet.tsx +143 -143
- package/vendor/chatgpt2api/source/web/src/components/ui/table.tsx +29 -29
- package/vendor/chatgpt2api/source/web/src/components/ui/tabs.tsx +91 -91
- package/vendor/chatgpt2api/source/web/src/components/ui/text-3d-flip.tsx +269 -269
- package/vendor/chatgpt2api/source/web/src/components/ui/textarea.tsx +18 -18
- package/vendor/chatgpt2api/source/web/src/components/version-release-dialog.tsx +127 -127
- package/vendor/chatgpt2api/source/web/src/constants/common-env.ts +6 -6
- package/vendor/chatgpt2api/source/web/src/hooks/use-version-check.ts +85 -85
- package/vendor/chatgpt2api/source/web/src/lib/api.ts +926 -926
- package/vendor/chatgpt2api/source/web/src/lib/auth-session.ts +26 -26
- package/vendor/chatgpt2api/source/web/src/lib/release.ts +25 -25
- package/vendor/chatgpt2api/source/web/src/lib/request.ts +93 -93
- package/vendor/chatgpt2api/source/web/src/lib/use-auth-guard.ts +89 -89
- package/vendor/chatgpt2api/source/web/src/lib/utils.ts +6 -6
- package/vendor/chatgpt2api/source/web/src/store/auth.ts +112 -112
- package/vendor/chatgpt2api/source/web/src/store/editable-file-history.ts +33 -33
- package/vendor/chatgpt2api/source/web/src/store/image-conversations.ts +318 -318
- package/vendor/chatgpt2api/source/web/tsconfig.json +42 -42
package/install.mjs
CHANGED
|
@@ -1,387 +1,333 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 把插件挂进当前 DSH desktop profile。
|
|
3
|
-
*
|
|
4
|
-
* 用法:
|
|
5
|
-
* node install.mjs 安装(幂等,可在任意目录重复执行)
|
|
6
|
-
* node install.mjs --uninstall 从 profile 配置里摘掉插件并断开目录联接
|
|
7
|
-
*
|
|
8
|
-
* 改动范围:
|
|
9
|
-
* 1. profile/node_modules 下建指向插件源码目录的目录联接(父目录自动创建);
|
|
10
|
-
* 2. profile/package.json 的 dependencies 与 dsh.profile.bundles(改前备份);
|
|
11
|
-
* 3. pnpm-lock.yaml 里直接补/删 importer 记录,不联网、不动 node_modules。
|
|
12
|
-
* 不会执行完整 pnpm install(避免重拉已装的其他插件),也不会重启 DSH。
|
|
13
|
-
*
|
|
14
|
-
* 顺序是「先建联接、后写配置」:联接建不起来时 profile 保持原样,DSH 照常启动,
|
|
15
|
-
* 不会出现「配置已注册、目录联接没建」的半装状态。这一点在全新 profile
|
|
16
|
-
* (没有 node_modules、没有 pnpm-lock.yaml)上尤其重要。
|
|
17
|
-
*
|
|
18
|
-
* 号池已经并入工作台:安装时顺手把旧的独立号池插件 dsh-plugin-image-pool
|
|
19
|
-
* 从 profile 摘掉(只改配置,不删它的源码目录和账号数据),侧栏就只剩一个
|
|
20
|
-
* 工作台入口。旧插件下次 DSH 启动不再加载,号池改由工作台后端自己拉起。
|
|
21
|
-
*
|
|
22
|
-
* 整包自包含:插件自带 vendor/chatgpt2api(号池服务端源码 + uv)与 server/
|
|
23
|
-
* (工作台后端代码)。开发机上仍优先用工作区里的 image-local/,把这个插件
|
|
24
|
-
* 单独拷到另一台机器时会自动落到 server/,不要求对方装任何第三方插件。
|
|
25
|
-
*/
|
|
26
|
-
import fs from 'node:fs';
|
|
27
|
-
import path from 'node:path';
|
|
28
|
-
import os from 'node:os';
|
|
29
|
-
import { fileURLToPath } from 'node:url';
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
console.error('
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
console.error('
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
try { fs.
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* -
|
|
84
|
-
* -
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
fs.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
console.error('
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
console.error('
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
console.error('
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
console.error('
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
fs.writeFileSync(
|
|
179
|
-
|
|
180
|
-
console.log('
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
*
|
|
186
|
-
* lock
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (
|
|
200
|
-
if (
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
fs.writeFileSync(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
fs.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
if (
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (
|
|
329
|
-
console.log(
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
function verify() {
|
|
335
|
-
const problems = [];
|
|
336
|
-
if (!bundles.includes(NAME)) problems.push('bundles 中未包含 ' + NAME);
|
|
337
|
-
if (!deps[NAME]) problems.push('dependencies 中未包含 ' + NAME);
|
|
338
|
-
if (linkState() !== 'ok') problems.push('目录联接不有效:' + LINK_PATH);
|
|
339
|
-
const viaLink = path.join(LINK_PATH, 'host.js');
|
|
340
|
-
if (!fs.existsSync(viaLink)) problems.push('通过目录联接读不到 host.js');
|
|
341
|
-
if (problems.length) {
|
|
342
|
-
console.error('自检未通过:');
|
|
343
|
-
for (const p of problems) console.error(' - ' + p);
|
|
344
|
-
return false;
|
|
345
|
-
}
|
|
346
|
-
console.log('自检通过:配置已注册,目录联接有效,插件文件可读。');
|
|
347
|
-
return true;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
/* ---------- 主流程 ---------- */
|
|
351
|
-
|
|
352
|
-
async function install() {
|
|
353
|
-
checkAssets();
|
|
354
|
-
syncServer();
|
|
355
|
-
ensureLink(); // 先建联接:失败则 profile 原样,不留半装
|
|
356
|
-
applyConfig(); // 再改配置
|
|
357
|
-
syncLock('link:' + PLUGIN_DIR.replace(/\\/g, '/'));
|
|
358
|
-
const ok = verify();
|
|
359
|
-
if (removedLegacy)
|
|
360
|
-
console.log('已摘除旧独立号池插件 ' + LEGACY + '(源码与账号数据保留,未删除)。');
|
|
361
|
-
await fetchStyleImages();
|
|
362
|
-
if (!ok) {
|
|
363
|
-
console.error('安装未完全成功,请根据上面的提示处理后重跑 node install.mjs。');
|
|
364
|
-
process.exit(1);
|
|
365
|
-
}
|
|
366
|
-
console.log('安装完成。多数情况下 DSH 会直接加载新 bundle;若侧栏未出现入口,完全退出 DSH 再启动一次。');
|
|
367
|
-
console.log('首次打开工作台会自动初始化后端与账号池,通常 1 分钟内就绪。');
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (!uninstall) {
|
|
371
|
-
install().catch(e => {
|
|
372
|
-
console.error('安装过程中发生异常:' + (e && e.stack || e));
|
|
373
|
-
process.exit(1);
|
|
374
|
-
});
|
|
375
|
-
} else {
|
|
376
|
-
applyConfig();
|
|
377
|
-
syncLock('link:' + PLUGIN_DIR.replace(/\\/g, '/'));
|
|
378
|
-
// 断开联接:只删联接本身,不会碰插件源码目录与账号数据。
|
|
379
|
-
const st = linkState();
|
|
380
|
-
if (st === 'ok' || st === 'dead') {
|
|
381
|
-
console.log(removeLink() ? '已断开目录联接 -> ' + LINK_PATH : '警告:目录联接未能删除,可手动移除。');
|
|
382
|
-
} else if (st === 'dir') {
|
|
383
|
-
console.log('node_modules/' + NAME + ' 是真实目录,非联接,未做改动。');
|
|
384
|
-
}
|
|
385
|
-
console.log('已从 profile 摘除插件;插件源码目录与 ~/.dsh/chatgpt2api-image 数据保留。');
|
|
386
|
-
console.log('完全退出 DSH 再启动,侧栏入口即消失。确认不再需要时可自行删除源码目录。');
|
|
387
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 把插件挂进当前 DSH desktop profile。
|
|
3
|
+
*
|
|
4
|
+
* 用法:
|
|
5
|
+
* node install.mjs 安装(幂等,可在任意目录重复执行)
|
|
6
|
+
* node install.mjs --uninstall 从 profile 配置里摘掉插件并断开目录联接
|
|
7
|
+
*
|
|
8
|
+
* 改动范围:
|
|
9
|
+
* 1. profile/node_modules 下建指向插件源码目录的目录联接(父目录自动创建);
|
|
10
|
+
* 2. profile/package.json 的 dependencies 与 dsh.profile.bundles(改前备份);
|
|
11
|
+
* 3. pnpm-lock.yaml 里直接补/删 importer 记录,不联网、不动 node_modules。
|
|
12
|
+
* 不会执行完整 pnpm install(避免重拉已装的其他插件),也不会重启 DSH。
|
|
13
|
+
*
|
|
14
|
+
* 顺序是「先建联接、后写配置」:联接建不起来时 profile 保持原样,DSH 照常启动,
|
|
15
|
+
* 不会出现「配置已注册、目录联接没建」的半装状态。这一点在全新 profile
|
|
16
|
+
* (没有 node_modules、没有 pnpm-lock.yaml)上尤其重要。
|
|
17
|
+
*
|
|
18
|
+
* 号池已经并入工作台:安装时顺手把旧的独立号池插件 dsh-plugin-image-pool
|
|
19
|
+
* 从 profile 摘掉(只改配置,不删它的源码目录和账号数据),侧栏就只剩一个
|
|
20
|
+
* 工作台入口。旧插件下次 DSH 启动不再加载,号池改由工作台后端自己拉起。
|
|
21
|
+
*
|
|
22
|
+
* 整包自包含:插件自带 vendor/chatgpt2api(号池服务端源码 + uv)与 server/
|
|
23
|
+
* (工作台后端代码)。开发机上仍优先用工作区里的 image-local/,把这个插件
|
|
24
|
+
* 单独拷到另一台机器时会自动落到 server/,不要求对方装任何第三方插件。
|
|
25
|
+
*/
|
|
26
|
+
import fs from 'node:fs';
|
|
27
|
+
import path from 'node:path';
|
|
28
|
+
import os from 'node:os';
|
|
29
|
+
import { fileURLToPath } from 'node:url';
|
|
30
|
+
import { fetchStyleImages as fetchSamples } from './scripts/fetch-style-images.mjs';
|
|
31
|
+
|
|
32
|
+
const NAME = 'dsh-plugin-image-workbench';
|
|
33
|
+
const LEGACY = 'dsh-plugin-image-pool';
|
|
34
|
+
const PLUGIN_DIR = path.resolve(fileURLToPath(new URL('.', import.meta.url)));
|
|
35
|
+
const PROFILE_DIR = path.join(process.env.DSH_HOME || path.join(os.homedir(), '.dsh'), 'profiles', 'desktop');
|
|
36
|
+
const PKG_FILE = path.join(PROFILE_DIR, 'package.json');
|
|
37
|
+
const LOCK_FILE = path.join(PROFILE_DIR, 'pnpm-lock.yaml');
|
|
38
|
+
const LINK_PATH = path.join(PROFILE_DIR, 'node_modules', NAME);
|
|
39
|
+
const uninstall = process.argv.includes('--uninstall');
|
|
40
|
+
|
|
41
|
+
/* Node 16 及更早没有全局 fetch,脚本会在改完配置之后才报错,留下半装状态。
|
|
42
|
+
这里先拦一道,报错时 profile 一个字节都没动。 */
|
|
43
|
+
const nodeMajor = Number(String(process.versions.node).split('.')[0]);
|
|
44
|
+
if (nodeMajor < 18) {
|
|
45
|
+
console.error('当前 Node 版本为 ' + process.versions.node + ',本安装器需要 Node 18 及以上(建议 20+)。');
|
|
46
|
+
console.error('升级 Node 后重新执行;profile 未被修改。');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!fs.existsSync(PKG_FILE)) {
|
|
51
|
+
console.error('找不到 DSH profile:' + PKG_FILE);
|
|
52
|
+
console.error('请确认 DSH 桌面端已至少启动过一次,或用 DSH_HOME 指定数据目录。');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const raw = fs.readFileSync(PKG_FILE, 'utf8');
|
|
57
|
+
const pkg = JSON.parse(raw);
|
|
58
|
+
const deps = pkg.dependencies || (pkg.dependencies = {});
|
|
59
|
+
const bundles = pkg.dsh?.profile?.bundles;
|
|
60
|
+
if (!Array.isArray(bundles)) {
|
|
61
|
+
console.error('profile/package.json 缺少 dsh.profile.bundles,已停止,不做任何修改。');
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* ---------- 目录联接 ---------- */
|
|
66
|
+
|
|
67
|
+
/** 判定当前联接状态:missing 不存在 / dir 是真实目录 / ok 有效联接 / dead 失效联接 */
|
|
68
|
+
function linkState() {
|
|
69
|
+
let st = null;
|
|
70
|
+
try { st = fs.lstatSync(LINK_PATH); } catch { return 'missing'; }
|
|
71
|
+
if (!st.isSymbolicLink()) return 'dir';
|
|
72
|
+
try { fs.realpathSync(LINK_PATH); return 'ok'; } catch { return 'dead'; }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function removeLink() {
|
|
76
|
+
try { fs.unlinkSync(LINK_PATH); return true; } catch (_) { /* 试下面的 */ }
|
|
77
|
+
try { fs.rmdirSync(LINK_PATH); return true; } catch (_) { return false; }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 建立(或修正)目录联接。
|
|
82
|
+
* 三种非理想状态都必须处理,否则会出现「装上了但其实指向旧目录」的静默错误:
|
|
83
|
+
* - 父目录不存在(全新 profile) -> 自动创建
|
|
84
|
+
* - 指向别的路径(换目录重装) -> 删除后重建
|
|
85
|
+
* - 死链(旧目录已被删) -> 删除后重建
|
|
86
|
+
*/
|
|
87
|
+
function ensureLink() {
|
|
88
|
+
const parent = path.dirname(LINK_PATH);
|
|
89
|
+
const createdParent = !fs.existsSync(parent);
|
|
90
|
+
fs.mkdirSync(parent, { recursive: true });
|
|
91
|
+
if (createdParent) console.log('已创建目录 -> ' + parent);
|
|
92
|
+
|
|
93
|
+
const st = linkState();
|
|
94
|
+
if (st === 'dir') {
|
|
95
|
+
console.error('node_modules/' + NAME + ' 是一个真实目录,不是目录联接。');
|
|
96
|
+
console.error('为避免误删你的文件,安装器不做改动。请先手动移走它,再重新执行。');
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
if (st === 'ok') {
|
|
100
|
+
const cur = fs.realpathSync(LINK_PATH);
|
|
101
|
+
if (path.resolve(cur).toLowerCase() === path.resolve(PLUGIN_DIR).toLowerCase()) {
|
|
102
|
+
console.log('目录联接已存在,指向本目录 -> ' + LINK_PATH);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
console.log('目录联接原指向 ' + cur + ',与本次安装目录不同,正在重建。');
|
|
106
|
+
if (!removeLink()) {
|
|
107
|
+
console.error('无法删除旧联接:' + LINK_PATH);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
} else if (st === 'dead') {
|
|
111
|
+
console.log('发现失效的目录联接(目标已不存在),重建中。');
|
|
112
|
+
if (!removeLink()) {
|
|
113
|
+
console.error('无法删除失效联接:' + LINK_PATH);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
fs.symlinkSync(PLUGIN_DIR, LINK_PATH, 'junction');
|
|
120
|
+
} catch (e) {
|
|
121
|
+
console.error('建立目录联接失败:' + (e && e.message || e));
|
|
122
|
+
console.error('常见原因:profile 目录无写入权限,或系统禁用了目录联接。profile 未被修改。');
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
console.log('已建立目录联接 -> ' + LINK_PATH);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* ---------- 包完整性 ---------- */
|
|
129
|
+
|
|
130
|
+
function checkAssets() {
|
|
131
|
+
const critical = ['host.js', 'client.js', 'cordis.patch.yml',
|
|
132
|
+
'server/app.py', 'server/static/index.html', 'server/catalog.json'];
|
|
133
|
+
const optional = ['vendor/chatgpt2api/runtime/uv.exe', 'server/requirements.txt'];
|
|
134
|
+
const missing = critical.filter(f => !fs.existsSync(path.join(PLUGIN_DIR, f)));
|
|
135
|
+
if (missing.length) {
|
|
136
|
+
console.error('插件包不完整,缺少:' + missing.join('、'));
|
|
137
|
+
console.error('这通常是解压不完整或下载中断。请重新获取完整包后重试。');
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
for (const f of optional) {
|
|
141
|
+
if (!fs.existsSync(path.join(PLUGIN_DIR, f)))
|
|
142
|
+
console.warn('提醒:未找到 ' + f + ',首次启动时将改用系统 Python 或自动下载运行时。');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* ---------- profile 配置 ---------- */
|
|
147
|
+
|
|
148
|
+
let removedLegacy = false;
|
|
149
|
+
|
|
150
|
+
/** 写入 dependencies 与 bundles,仅在确有变化时备份并落盘。 */
|
|
151
|
+
function applyConfig() {
|
|
152
|
+
let changed = false;
|
|
153
|
+
if (uninstall) {
|
|
154
|
+
if (deps[NAME]) { delete deps[NAME]; changed = true; }
|
|
155
|
+
const i = bundles.indexOf(NAME);
|
|
156
|
+
if (i >= 0) { bundles.splice(i, 1); changed = true; }
|
|
157
|
+
} else {
|
|
158
|
+
const linkSpec = 'link:' + PLUGIN_DIR.replace(/\\/g, '/');
|
|
159
|
+
if (deps[NAME] !== linkSpec) { deps[NAME] = linkSpec; changed = true; }
|
|
160
|
+
if (!bundles.includes(NAME)) { bundles.push(NAME); changed = true; }
|
|
161
|
+
// 旧独立号池插件只从 bundle / 依赖里摘掉,文件与账号数据一律保留
|
|
162
|
+
if (deps[LEGACY]) { delete deps[LEGACY]; changed = true; removedLegacy = true; }
|
|
163
|
+
const j = bundles.indexOf(LEGACY);
|
|
164
|
+
if (j >= 0) { bundles.splice(j, 1); changed = true; removedLegacy = true; }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (!changed) { console.log('profile 配置已经是目标状态,无需修改。'); return; }
|
|
168
|
+
|
|
169
|
+
// 写文件之前再确认一次目标文件没被别的进程改掉,降低覆盖别人改动的风险。
|
|
170
|
+
if (fs.readFileSync(PKG_FILE, 'utf8') !== raw) {
|
|
171
|
+
console.error('profile/package.json 在安装过程中被其他进程修改,已停止以免覆盖。');
|
|
172
|
+
console.error('请关闭 DSH 后重新执行,或手动核对配置。');
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const stamp = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 14);
|
|
177
|
+
const backup = PKG_FILE + '.bak_' + stamp;
|
|
178
|
+
fs.writeFileSync(backup, raw, 'utf8');
|
|
179
|
+
fs.writeFileSync(PKG_FILE, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
|
|
180
|
+
console.log('已备份原配置 -> ' + backup);
|
|
181
|
+
console.log('已更新 -> ' + PKG_FILE);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 直接补/删 lock 的 importer 记录,不调用 pnpm install。
|
|
186
|
+
* link 依赖在 lock v9 里的标准写法(已实测,跨盘符时 specifier 与 version 都是绝对路径)。
|
|
187
|
+
* lock 不存在时跳过——DSH 下次执行 pnpm install 会自行生成。
|
|
188
|
+
*/
|
|
189
|
+
function dropLockEntry(text, name) {
|
|
190
|
+
const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
191
|
+
return text.replace(new RegExp(' ' + escaped + ':\\n(?: [^\\n]*\\n){2}'), '');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function syncLock(linkSpec) {
|
|
195
|
+
if (!fs.existsSync(LOCK_FILE)) return;
|
|
196
|
+
const before = fs.readFileSync(LOCK_FILE, 'utf8');
|
|
197
|
+
const entry = ' ' + NAME + ':\n specifier: ' + linkSpec + '\n version: ' + linkSpec + '\n';
|
|
198
|
+
let after = before;
|
|
199
|
+
if (uninstall) after = dropLockEntry(after, NAME);
|
|
200
|
+
if (removedLegacy) after = dropLockEntry(after, LEGACY);
|
|
201
|
+
if (!uninstall && !before.includes(' ' + NAME + ':')) {
|
|
202
|
+
const marker = '\npackages:\n';
|
|
203
|
+
const at = after.indexOf(marker);
|
|
204
|
+
if (at < 0) { console.warn('lock 结构不符合预期,跳过(不影响插件加载)。'); return; }
|
|
205
|
+
after = after.slice(0, at) + entry + after.slice(at);
|
|
206
|
+
}
|
|
207
|
+
if (after === before) return;
|
|
208
|
+
const backup = LOCK_FILE + '.bak_' + new Date().toISOString().replace(/[-:T]/g, '').slice(0, 14);
|
|
209
|
+
fs.writeFileSync(backup, before, 'utf8');
|
|
210
|
+
fs.writeFileSync(LOCK_FILE, after, 'utf8');
|
|
211
|
+
console.log('已备份并同步 -> ' + LOCK_FILE);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* ---------- 后端代码同步 ---------- */
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* 把开发目录 image-local/ 的纯代码同步进插件 server/。
|
|
218
|
+
* 只覆盖代码,绝不删目标里已有的图片、配置、数据库等运行态数据。
|
|
219
|
+
* 发布后的插件包旁边没有 image-local/,直接跳过,用包内自带的 server/。
|
|
220
|
+
*/
|
|
221
|
+
function syncServer() {
|
|
222
|
+
const src = path.resolve(PLUGIN_DIR, '..', 'image-local');
|
|
223
|
+
const dst = path.join(PLUGIN_DIR, 'server');
|
|
224
|
+
if (!fs.existsSync(path.join(src, 'app.py'))) {
|
|
225
|
+
console.log('旁边没有 image-local/,使用插件包内自带的 server/。');
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const skipDirs = new Set(['.venv', '__pycache__', 'images', 'thumbs', 'refs',
|
|
229
|
+
'repo_images', 'workbench', '.pytest_cache']);
|
|
230
|
+
const skipFiles = new Set(['config.local.json', 'app.db', 'history.db', '.DS_Store']);
|
|
231
|
+
const junk = n => skipFiles.has(n) || /\.(log|pyc|db|vps_original)$/.test(n)
|
|
232
|
+
|| /\.bak_/.test(n);
|
|
233
|
+
// 开发机上的源文件可能是 CRLF,仓库里存的是 LF。不归一的话每次同步
|
|
234
|
+
// 都会把整个文件标成改动,commit 里几千行假 diff,历史没法看。
|
|
235
|
+
const textExt = new Set(['.py', '.html', '.js', '.mjs', '.json', '.txt',
|
|
236
|
+
'.md', '.css', '.yml', '.yaml']);
|
|
237
|
+
const syncFile = (from, to) => {
|
|
238
|
+
if (!textExt.has(path.extname(from).toLowerCase())) {
|
|
239
|
+
fs.copyFileSync(from, to);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const s = fs.readFileSync(from, 'utf8').replace(/\r\n?/g, '\n');
|
|
243
|
+
fs.writeFileSync(to, s, 'utf8');
|
|
244
|
+
};
|
|
245
|
+
let copied = 0;
|
|
246
|
+
const walk = (from, to) => {
|
|
247
|
+
fs.mkdirSync(to, { recursive: true });
|
|
248
|
+
for (const e of fs.readdirSync(from, { withFileTypes: true })) {
|
|
249
|
+
const a = path.join(from, e.name), b = path.join(to, e.name);
|
|
250
|
+
if (e.isDirectory()) { if (!skipDirs.has(e.name)) walk(a, b); continue; }
|
|
251
|
+
if (!e.isFile() || junk(e.name)) continue;
|
|
252
|
+
syncFile(a, b);
|
|
253
|
+
copied += 1;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
walk(src, dst);
|
|
257
|
+
fs.writeFileSync(path.join(dst, 'requirements.txt'),
|
|
258
|
+
'fastapi\nuvicorn\nhttpx\npydantic\npillow\npython-multipart\n', 'utf8');
|
|
259
|
+
console.log('已同步工作台后端代码 -> server/(' + copied + ' 个文件,不含账号/图片/配置)。');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* ---------- 样图下载 ---------- */
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* 安装时补齐真实样图(风格 / 图型 / 主题色预览)。
|
|
266
|
+
* 实现与 host.js 共用 scripts/fetch-style-images.mjs —— 一份逻辑两处调用,
|
|
267
|
+
* 免得「插件启动时补图」和「安装时补图」两套代码各自漂移。
|
|
268
|
+
*/
|
|
269
|
+
async function fetchStyleImages() {
|
|
270
|
+
const r = await fetchSamples({
|
|
271
|
+
serverDir: path.join(PLUGIN_DIR, 'server'),
|
|
272
|
+
log: text => console.log(text),
|
|
273
|
+
});
|
|
274
|
+
if (r.error) console.log('样图下载跳过:' + r.error);
|
|
275
|
+
if (r.fail) console.log(' ' + r.fail + ' 张失败(多为网络受限)。重跑 node install.mjs 可补齐,已存在的会自动跳过。');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/* ---------- 安装后自检 ---------- */
|
|
279
|
+
|
|
280
|
+
function verify() {
|
|
281
|
+
const problems = [];
|
|
282
|
+
if (!bundles.includes(NAME)) problems.push('bundles 中未包含 ' + NAME);
|
|
283
|
+
if (!deps[NAME]) problems.push('dependencies 中未包含 ' + NAME);
|
|
284
|
+
if (linkState() !== 'ok') problems.push('目录联接不有效:' + LINK_PATH);
|
|
285
|
+
const viaLink = path.join(LINK_PATH, 'host.js');
|
|
286
|
+
if (!fs.existsSync(viaLink)) problems.push('通过目录联接读不到 host.js');
|
|
287
|
+
if (problems.length) {
|
|
288
|
+
console.error('自检未通过:');
|
|
289
|
+
for (const p of problems) console.error(' - ' + p);
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
console.log('自检通过:配置已注册,目录联接有效,插件文件可读。');
|
|
293
|
+
return true;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* ---------- 主流程 ---------- */
|
|
297
|
+
|
|
298
|
+
async function install() {
|
|
299
|
+
checkAssets();
|
|
300
|
+
syncServer();
|
|
301
|
+
ensureLink(); // 先建联接:失败则 profile 原样,不留半装
|
|
302
|
+
applyConfig(); // 再改配置
|
|
303
|
+
syncLock('link:' + PLUGIN_DIR.replace(/\\/g, '/'));
|
|
304
|
+
const ok = verify();
|
|
305
|
+
if (removedLegacy)
|
|
306
|
+
console.log('已摘除旧独立号池插件 ' + LEGACY + '(源码与账号数据保留,未删除)。');
|
|
307
|
+
await fetchStyleImages();
|
|
308
|
+
if (!ok) {
|
|
309
|
+
console.error('安装未完全成功,请根据上面的提示处理后重跑 node install.mjs。');
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
console.log('安装完成。多数情况下 DSH 会直接加载新 bundle;若侧栏未出现入口,完全退出 DSH 再启动一次。');
|
|
313
|
+
console.log('首次打开工作台会自动初始化后端与账号池,通常 1 分钟内就绪。');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (!uninstall) {
|
|
317
|
+
install().catch(e => {
|
|
318
|
+
console.error('安装过程中发生异常:' + (e && e.stack || e));
|
|
319
|
+
process.exit(1);
|
|
320
|
+
});
|
|
321
|
+
} else {
|
|
322
|
+
applyConfig();
|
|
323
|
+
syncLock('link:' + PLUGIN_DIR.replace(/\\/g, '/'));
|
|
324
|
+
// 断开联接:只删联接本身,不会碰插件源码目录与账号数据。
|
|
325
|
+
const st = linkState();
|
|
326
|
+
if (st === 'ok' || st === 'dead') {
|
|
327
|
+
console.log(removeLink() ? '已断开目录联接 -> ' + LINK_PATH : '警告:目录联接未能删除,可手动移除。');
|
|
328
|
+
} else if (st === 'dir') {
|
|
329
|
+
console.log('node_modules/' + NAME + ' 是真实目录,非联接,未做改动。');
|
|
330
|
+
}
|
|
331
|
+
console.log('已从 profile 摘除插件;插件源码目录与 ~/.dsh/chatgpt2api-image 数据保留。');
|
|
332
|
+
console.log('完全退出 DSH 再启动,侧栏入口即消失。确认不再需要时可自行删除源码目录。');
|
|
333
|
+
}
|