@jetcrab/pi-desk 1.1.34-dev → 1.1.38-dev
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/README.zh-CN.md +10 -10
- package/docs/commands.md +13 -2
- package/docs/plugins.md +10 -0
- package/package.json +3 -3
- package/src/client/l2_biz/pi-settings/l2-pi-settings-locale.ts +3 -3
- package/src/client/l2_biz/plugin-host/l2-plugin-host-runtime.ts +5 -6
- package/src/client/l4_foundation/plugin-host/l4-plugin-host-runtime.ts +20 -2
- package/src/common/l2_biz/plugin/l2-plugin-management-messages.ts +29 -17
- package/src/common/l2_biz/project-preview/l2-project-history-messages.ts +9 -7
- package/src/common/l2_biz/settings/l2-settings-messages.ts +82 -69
- package/src/common/l2_biz/terminal/l2-terminal-locale-messages.ts +1 -1
- package/src/common/l2_biz/workbench/l2-workbench-locale-messages.ts +48 -38
- package/src/common/l2_biz/workbench/l2-workbench-picker-messages.ts +12 -8
- package/src/common/l3_modules/conversation/l3-conversation-locale-messages.ts +24 -20
- package/src/common/l3_modules/project-files/l3-project-files-locale-messages.ts +29 -17
- package/src/common/l4_foundation/locale/l4-host-error-messages.ts +5 -5
- package/src/server/l2_biz/plugin-management/l2-plugin-commands.ts +8 -7
- package/src/server/l2_biz/plugin-management/l2-plugin-management.ts +51 -2
- package/src/server/l4_foundation/pi/l4-pi-global-plugin-runtime.ts +9 -1
- package/src/server/l4_foundation/pi/l4-pi-local-extension-maintenance.ts +118 -0
- package/src/server/l4_foundation/pi/l4-pi-plugin-owner-runtime.ts +4 -1
- package/src/server/l4_foundation/pi/l4-pi-plugin-source-changes.ts +4 -0
- package/src/server/l4_foundation/pidesk/l4-pidesk-command.ts +1 -1
- package/temp/build/pi-desk/release/.next/BUILD_ID +1 -1
- package/temp/build/pi-desk/release/.next/build-manifest.json +2 -2
- package/temp/build/pi-desk/release/.next/required-server-files.js +2 -2
- package/temp/build/pi-desk/release/.next/required-server-files.json +2 -2
- package/temp/build/pi-desk/release/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.html +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.rsc +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/health/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/page.js +3 -3
- package/temp/build/pi-desk/release/.next/server/app/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/project-preview/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/16438.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/25406.js +2 -2
- package/temp/build/pi-desk/release/.next/server/chunks/35985.js +2 -2
- package/temp/build/pi-desk/release/.next/server/chunks/36361.js +3 -3
- package/temp/build/pi-desk/release/.next/server/chunks/47757.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/50520.js +2 -2
- package/temp/build/pi-desk/release/.next/server/chunks/84231.js +3 -3
- package/temp/build/pi-desk/release/.next/server/chunks/{18748.js → 88353.js} +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/92386.js +4 -4
- package/temp/build/pi-desk/release/.next/server/instrumentation.js +1 -1
- package/temp/build/pi-desk/release/.next/server/middleware-build-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/pages/500.html +1 -1
- package/temp/build/pi-desk/release/.next/static/chunks/11937-c21edad81915b999.js +1 -0
- package/temp/build/pi-desk/release/.next/static/chunks/15302-e9fd56a464b5267b.js +1 -0
- package/temp/build/pi-desk/release/.next/static/chunks/app/layout-1ec03fc5766568f6.js +1 -0
- package/temp/build/pi-desk/release/.next/static/chunks/app/{page-468db7c29da3cdf6.js → page-402842ab5d6afb08.js} +3 -3
- package/temp/build/pi-desk/release/.next/trace-build +1 -1
- package/temp/build/pi-desk/release/.next/static/chunks/11937-d5d4d2199356c3ed.js +0 -1
- package/temp/build/pi-desk/release/.next/static/chunks/15302-6e070e13fa65c863.js +0 -1
- package/temp/build/pi-desk/release/.next/static/chunks/app/layout-fdea504082053156.js +0 -1
- /package/temp/build/pi-desk/release/.next/static/{SEeF4XXo-IRzX6G2WyyuJ → ljXlpZIQpD6BvWyD8Fmc3}/_buildManifest.js +0 -0
- /package/temp/build/pi-desk/release/.next/static/{SEeF4XXo-IRzX6G2WyyuJ → ljXlpZIQpD6BvWyD8Fmc3}/_ssgManifest.js +0 -0
|
@@ -7,7 +7,7 @@ export const L3_CONVERSATION_LOCALE_MESSAGES = {
|
|
|
7
7
|
unknownModel: 'Unknown model',
|
|
8
8
|
input: 'Input',
|
|
9
9
|
output: 'Output',
|
|
10
|
-
cached: '
|
|
10
|
+
cached: 'Cached input',
|
|
11
11
|
cost: 'Cost',
|
|
12
12
|
error: 'Failed',
|
|
13
13
|
aborted: 'Aborted',
|
|
@@ -17,7 +17,8 @@ export const L3_CONVERSATION_LOCALE_MESSAGES = {
|
|
|
17
17
|
nestedCallsIncomplete: 'Nested calls · Incomplete record',
|
|
18
18
|
nestedStatus: { ok: 'Completed', error: 'Failed', unfinished: 'Unfinished' },
|
|
19
19
|
nestedArguments: 'Arguments',
|
|
20
|
-
|
|
20
|
+
nestedArgumentsBytes_one: 'Arguments: {{count}} byte',
|
|
21
|
+
nestedArgumentsBytes_other: 'Arguments: {{count}} bytes',
|
|
21
22
|
image: 'Markdown image',
|
|
22
23
|
imageUnavailable: 'Image unavailable',
|
|
23
24
|
previewImage: 'Preview image: {{name}}',
|
|
@@ -36,11 +37,12 @@ export const L3_CONVERSATION_LOCALE_MESSAGES = {
|
|
|
36
37
|
expand: 'Show full message',
|
|
37
38
|
collapse: 'Collapse',
|
|
38
39
|
thinking: 'Thinking…',
|
|
39
|
-
thinkingLoading: 'Loading
|
|
40
|
-
thinkingUnavailable: '
|
|
41
|
-
thinkingProcess: '
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
thinkingLoading: 'Loading thinking content…',
|
|
41
|
+
thinkingUnavailable: 'Thinking content unavailable',
|
|
42
|
+
thinkingProcess: 'Thinking',
|
|
43
|
+
thinkingTruncated_one: 'Showing the last {{count}} character',
|
|
44
|
+
thinkingTruncated_other: 'Showing the last {{count}} characters',
|
|
45
|
+
copyThinking: 'Copy thinking content',
|
|
44
46
|
streamInterrupted: 'The response stream ended before the model completed its answer.',
|
|
45
47
|
requestTimedOut: 'The model did not complete its response in time.',
|
|
46
48
|
modelFailed: 'The model request failed without a detailed error.',
|
|
@@ -60,7 +62,7 @@ export const L3_CONVERSATION_LOCALE_MESSAGES = {
|
|
|
60
62
|
pideskListPlugins: 'View plugins',
|
|
61
63
|
pideskShowPlugin: 'View plugin',
|
|
62
64
|
pideskInstallPlugin: 'Install plugin',
|
|
63
|
-
pideskRemovePlugin: '
|
|
65
|
+
pideskRemovePlugin: 'Uninstall plugin',
|
|
64
66
|
pidesk: {
|
|
65
67
|
command: 'Command',
|
|
66
68
|
output: 'Output',
|
|
@@ -69,8 +71,8 @@ export const L3_CONVERSATION_LOCALE_MESSAGES = {
|
|
|
69
71
|
empty: 'No output',
|
|
70
72
|
noPlugins: 'No plugins',
|
|
71
73
|
unknownVersion: 'Version unknown',
|
|
72
|
-
updateChannel: '
|
|
73
|
-
targetVersion: '
|
|
74
|
+
updateChannel: 'Update channel: {{tag}}',
|
|
75
|
+
targetVersion: 'Target version: {{version}}',
|
|
74
76
|
restartRequired: 'Pi Desk needs to be restarted',
|
|
75
77
|
pluginDetail: 'Plugin details',
|
|
76
78
|
info: {
|
|
@@ -90,7 +92,7 @@ export const L3_CONVERSATION_LOCALE_MESSAGES = {
|
|
|
90
92
|
disabled: 'Disabled',
|
|
91
93
|
failed: 'Load failed'
|
|
92
94
|
},
|
|
93
|
-
action: { add: 'Install', update: 'Update', del: '
|
|
95
|
+
action: { add: 'Install', update: 'Update', del: 'Uninstall', apply: 'Load changes' },
|
|
94
96
|
phase: {
|
|
95
97
|
queued: 'Queued',
|
|
96
98
|
checking: 'Checking',
|
|
@@ -100,7 +102,7 @@ export const L3_CONVERSATION_LOCALE_MESSAGES = {
|
|
|
100
102
|
},
|
|
101
103
|
errorPhase: {
|
|
102
104
|
package: 'Package',
|
|
103
|
-
entry: 'Entry',
|
|
105
|
+
entry: 'Entry point',
|
|
104
106
|
setup: 'Initialization',
|
|
105
107
|
resources: 'Resources',
|
|
106
108
|
resolve: 'Resolution',
|
|
@@ -121,12 +123,12 @@ export const L3_CONVERSATION_LOCALE_MESSAGES = {
|
|
|
121
123
|
writeContent: 'Written file content',
|
|
122
124
|
writeResult: 'Write tool result',
|
|
123
125
|
emptyMessage: 'Empty message',
|
|
124
|
-
compactionSummary: '
|
|
125
|
-
compactionSummaryContent: '
|
|
126
|
-
compactionRunning: '
|
|
127
|
-
compactionComplete: 'Context
|
|
128
|
-
compactionLoading: 'Loading
|
|
129
|
-
compactionUnavailable: '
|
|
126
|
+
compactionSummary: 'Compaction summary',
|
|
127
|
+
compactionSummaryContent: 'Compaction summary content',
|
|
128
|
+
compactionRunning: 'Compacting context…',
|
|
129
|
+
compactionComplete: 'Context compacted',
|
|
130
|
+
compactionLoading: 'Loading compaction summary…',
|
|
131
|
+
compactionUnavailable: 'Compaction summary unavailable',
|
|
130
132
|
processGroup: 'Activity',
|
|
131
133
|
toolCalls_one: '{{count}} tool call',
|
|
132
134
|
toolCalls_other: '{{count}} tool calls',
|
|
@@ -165,7 +167,8 @@ export const L3_CONVERSATION_LOCALE_MESSAGES = {
|
|
|
165
167
|
nestedCallsIncomplete: '嵌套调用 · 记录未完整',
|
|
166
168
|
nestedStatus: { ok: '完成', error: '失败', unfinished: '未完成' },
|
|
167
169
|
nestedArguments: '调用参数',
|
|
168
|
-
|
|
170
|
+
nestedArgumentsBytes_one: '参数 {{count}} 字节',
|
|
171
|
+
nestedArgumentsBytes_other: '参数 {{count}} 字节',
|
|
169
172
|
image: 'Markdown 图片',
|
|
170
173
|
imageUnavailable: '图片暂不可用',
|
|
171
174
|
previewImage: '预览图片:{{name}}',
|
|
@@ -187,7 +190,8 @@ export const L3_CONVERSATION_LOCALE_MESSAGES = {
|
|
|
187
190
|
thinkingLoading: '正在加载思考内容…',
|
|
188
191
|
thinkingUnavailable: '思考内容不可用',
|
|
189
192
|
thinkingProcess: '思考过程',
|
|
190
|
-
|
|
193
|
+
thinkingTruncated_one: '仅显示最近 {{count}} 个字符',
|
|
194
|
+
thinkingTruncated_other: '仅显示最近 {{count}} 个字符',
|
|
191
195
|
copyThinking: '复制完整思考',
|
|
192
196
|
streamInterrupted: '模型响应流意外中断:连接在回答完成前被关闭。',
|
|
193
197
|
requestTimedOut: '模型请求超时:在限定时间内未收到完整响应。',
|
|
@@ -33,18 +33,19 @@ export const L3_PROJECT_FILES_LOCALE_MESSAGES = {
|
|
|
33
33
|
searchStart: 'Search this project',
|
|
34
34
|
searchTryOptions: 'Try another query or adjust the search options.',
|
|
35
35
|
searchFailed: 'Could not search the project. Please try again.',
|
|
36
|
-
searchCounts: '{{files}}
|
|
36
|
+
searchCounts: 'Files: {{files}} · Matching lines: {{lines}}',
|
|
37
37
|
searchShownCounts: 'Shown: {{counts}}',
|
|
38
38
|
searchFileHeading: 'Filenames',
|
|
39
39
|
searchContentHeading: 'Content',
|
|
40
|
-
searchContentCounts: '{{lines}}
|
|
40
|
+
searchContentCounts: 'Matching lines: {{lines}} · Files: {{files}}',
|
|
41
41
|
searchProjectRoot: 'Project root',
|
|
42
|
-
|
|
42
|
+
searchExpandFiles_one: 'Show {{count}} more file',
|
|
43
|
+
searchExpandFiles_other: 'Show {{count}} more files',
|
|
43
44
|
searchOpenLine: 'Open {{path}}, line {{line}}',
|
|
44
45
|
searchLineUnavailable: 'The file content has changed and the matching line no longer exists.',
|
|
45
46
|
searchTextLimit: 'Content search: previewable UTF-8 text up to 5 MiB.',
|
|
46
47
|
closeSearch: 'Exit search',
|
|
47
|
-
layout: '
|
|
48
|
+
layout: 'Group files by',
|
|
48
49
|
directory: 'Folders',
|
|
49
50
|
repository: 'Repositories',
|
|
50
51
|
changesOnly: 'Changes only',
|
|
@@ -72,14 +73,19 @@ export const L3_PROJECT_FILES_LOCALE_MESSAGES = {
|
|
|
72
73
|
copyContentFailed: 'Could not copy the file. Please copy it manually.',
|
|
73
74
|
fileActions: 'File actions',
|
|
74
75
|
headToWorking: 'HEAD ↔ working tree',
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
gitChangesCount_one: 'Against HEAD · {{count}} change',
|
|
77
|
+
gitChangesCount_other: 'Against HEAD · {{count}} changes',
|
|
78
|
+
gitChangesCompact_one: '{{count}} change',
|
|
79
|
+
gitChangesCompact_other: '{{count}} changes',
|
|
77
80
|
gitMarkersLoading: 'Comparing with HEAD…',
|
|
78
81
|
gitMarkersFailed: 'Could not read changes · Reload to retry',
|
|
79
82
|
gitMarkersUnavailable: 'Line change markers unavailable',
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
gitAddedLines_one: '{{count}} line added',
|
|
84
|
+
gitAddedLines_other: '{{count}} lines added',
|
|
85
|
+
gitModifiedLines_one: '{{count}} line modified',
|
|
86
|
+
gitModifiedLines_other: '{{count}} lines modified',
|
|
87
|
+
gitDeletedLines_one: '{{count}} line deleted',
|
|
88
|
+
gitDeletedLines_other: '{{count}} lines deleted',
|
|
83
89
|
noDiffChanges: ' · No content differences',
|
|
84
90
|
previousChange: 'Previous change',
|
|
85
91
|
nextChange: 'Next change',
|
|
@@ -99,8 +105,8 @@ export const L3_PROJECT_FILES_LOCALE_MESSAGES = {
|
|
|
99
105
|
goToLine: 'Go to line',
|
|
100
106
|
copySelectionOrAll: 'Copy selected or all content',
|
|
101
107
|
reloadCurrentFile: 'Reload this file',
|
|
102
|
-
binaryDiffUnavailable: '
|
|
103
|
-
largeDiffUnavailable: '
|
|
108
|
+
binaryDiffUnavailable: 'Diff previews are not available for binary files.',
|
|
109
|
+
largeDiffUnavailable: 'Diff previews are not available for files over 5 MB.',
|
|
104
110
|
readAgain: 'Reload',
|
|
105
111
|
noOpenFile: 'No file open',
|
|
106
112
|
imageViewer: '{{name}} image viewer',
|
|
@@ -162,7 +168,8 @@ export const L3_PROJECT_FILES_LOCALE_MESSAGES = {
|
|
|
162
168
|
searchContentHeading: '内容',
|
|
163
169
|
searchContentCounts: '{{lines}} 个命中行 · {{files}} 个文件',
|
|
164
170
|
searchProjectRoot: '项目根目录',
|
|
165
|
-
|
|
171
|
+
searchExpandFiles_one: '展开其余 {{count}} 个文件',
|
|
172
|
+
searchExpandFiles_other: '展开其余 {{count}} 个文件',
|
|
166
173
|
searchOpenLine: '打开 {{path}} 第 {{line}} 行',
|
|
167
174
|
searchLineUnavailable: '文件内容已变化,搜索命中行已不存在',
|
|
168
175
|
searchTextLimit: '正文搜索:可预览的 UTF-8 文本,不超过 5 MiB。',
|
|
@@ -195,14 +202,19 @@ export const L3_PROJECT_FILES_LOCALE_MESSAGES = {
|
|
|
195
202
|
copyContentFailed: '复制文件内容失败,请手动复制。',
|
|
196
203
|
fileActions: '文件操作',
|
|
197
204
|
headToWorking: 'HEAD ↔ 工作区',
|
|
198
|
-
|
|
199
|
-
|
|
205
|
+
gitChangesCount_one: '相对 HEAD · {{count}} 处变更',
|
|
206
|
+
gitChangesCount_other: '相对 HEAD · {{count}} 处变更',
|
|
207
|
+
gitChangesCompact_one: '{{count}} 处变更',
|
|
208
|
+
gitChangesCompact_other: '{{count}} 处变更',
|
|
200
209
|
gitMarkersLoading: '正在与 HEAD 比较…',
|
|
201
210
|
gitMarkersFailed: '变更读取失败 · 刷新重试',
|
|
202
211
|
gitMarkersUnavailable: '此文件无法显示行变更',
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
212
|
+
gitAddedLines_one: '新增 {{count}} 行',
|
|
213
|
+
gitAddedLines_other: '新增 {{count}} 行',
|
|
214
|
+
gitModifiedLines_one: '修改 {{count}} 行',
|
|
215
|
+
gitModifiedLines_other: '修改 {{count}} 行',
|
|
216
|
+
gitDeletedLines_one: '删除 {{count}} 行',
|
|
217
|
+
gitDeletedLines_other: '删除 {{count}} 行',
|
|
206
218
|
noDiffChanges: ' · 当前比较无内容差异',
|
|
207
219
|
previousChange: '上一个变更',
|
|
208
220
|
nextChange: '下一个变更',
|
|
@@ -69,9 +69,9 @@ export const L4_HOST_ERROR_MESSAGES = {
|
|
|
69
69
|
modelSettingsGetInvalid: 'Invalid model settings request',
|
|
70
70
|
modelSettingsGetFailed: 'Could not read model settings',
|
|
71
71
|
projectDefaultInputInvalid: 'Invalid project directory',
|
|
72
|
-
projectDefaultGetFailed: 'Could not read project model
|
|
73
|
-
projectDefaultSaveInvalid: 'Invalid project model
|
|
74
|
-
projectDefaultSaveFailed: 'Could not save project model
|
|
72
|
+
projectDefaultGetFailed: 'Could not read the project default model',
|
|
73
|
+
projectDefaultSaveInvalid: 'Invalid project default model',
|
|
74
|
+
projectDefaultSaveFailed: 'Could not save the project default model',
|
|
75
75
|
modelsListFailed: 'Could not list models',
|
|
76
76
|
directoryBrowseInvalid: 'Invalid directory browser request',
|
|
77
77
|
directoryMissing: 'Directory not found or is not a folder',
|
|
@@ -101,7 +101,7 @@ export const L4_HOST_ERROR_MESSAGES = {
|
|
|
101
101
|
sessionTreeEntryFailed: 'Could not read session tree entry',
|
|
102
102
|
branchInputInvalid: 'Invalid branch operation request',
|
|
103
103
|
branchEntryMissing: 'Branch entry not found or no longer available',
|
|
104
|
-
branchForkUserOnly: '
|
|
104
|
+
branchForkUserOnly: 'Sessions can only be forked from user messages',
|
|
105
105
|
branchResendUserOnly: 'Only user messages can be resent',
|
|
106
106
|
branchCloneEmpty: 'There are no messages to clone yet',
|
|
107
107
|
branchFailed: 'Could not perform branch operation',
|
|
@@ -137,7 +137,7 @@ export const L4_HOST_ERROR_MESSAGES = {
|
|
|
137
137
|
pluginResourceReadFailed: 'Could not read plugin browser resource',
|
|
138
138
|
hostResourceMissing: 'Host plugin resource not found',
|
|
139
139
|
hostRuntimeUnavailable: 'Host plugin runtime is not built',
|
|
140
|
-
hostResourceReadFailed: 'Could not read
|
|
140
|
+
hostResourceReadFailed: 'Could not read host plugin resource',
|
|
141
141
|
gitAddJsonInvalid: 'Invalid JSON for new Git branch',
|
|
142
142
|
gitAddInputInvalid: 'Invalid new Git branch request',
|
|
143
143
|
gitAddFailed: 'Could not create Git branch',
|
|
@@ -2,6 +2,7 @@ import 'server-only'
|
|
|
2
2
|
|
|
3
3
|
import { getAgentDir } from '@earendil-works/pi-coding-agent'
|
|
4
4
|
import { selectL4LocalizedText } from '@common/l4_foundation/locale/l4-localized-text'
|
|
5
|
+
import { l4PluginSourceIdentity } from '@common/l4_foundation/plugin/l4-plugin-package'
|
|
5
6
|
import type {
|
|
6
7
|
L2PluginManagementItem,
|
|
7
8
|
L2PluginManagementSnapshot
|
|
@@ -73,9 +74,6 @@ async function resolveTargets(
|
|
|
73
74
|
}
|
|
74
75
|
if (command.action === 'remove') {
|
|
75
76
|
if (!existing) throw new Error(`插件不存在:${name}`)
|
|
76
|
-
if (existing.kind !== 'package') {
|
|
77
|
-
throw new Error(`当前插件管理只支持卸载包来源,不支持卸载裸扩展:${name}`)
|
|
78
|
-
}
|
|
79
77
|
targets.set(existing.source, { name, source: existing.source, local: false })
|
|
80
78
|
continue
|
|
81
79
|
}
|
|
@@ -173,15 +171,18 @@ function completedResult(
|
|
|
173
171
|
snapshot: L2PluginManagementSnapshot,
|
|
174
172
|
agentDir: string
|
|
175
173
|
): string {
|
|
174
|
+
if (command.action === 'remove') {
|
|
175
|
+
const identity = l4PluginSourceIdentity(target.source)
|
|
176
|
+
if (snapshot.plugins.some((item) => l4PluginSourceIdentity(item.source) === identity)) {
|
|
177
|
+
throw new Error('卸载后仍发现该来源,无法确认卸载完成')
|
|
178
|
+
}
|
|
179
|
+
return '成功:已卸载并完成宿主插件应用'
|
|
180
|
+
}
|
|
176
181
|
const item = findL2PluginCommandTarget(
|
|
177
182
|
snapshot.plugins,
|
|
178
183
|
readL2PluginNpmName(target.source) ?? target.name,
|
|
179
184
|
agentDir
|
|
180
185
|
)
|
|
181
|
-
if (command.action === 'remove') {
|
|
182
|
-
if (item) throw new Error('卸载后仍发现该包来源,无法确认卸载完成')
|
|
183
|
-
return '成功:已卸载并完成宿主插件应用'
|
|
184
|
-
}
|
|
185
186
|
if (!item) throw new Error('未发现安装后的插件,无法确认生效')
|
|
186
187
|
if (item.error) throw new Error(selectL4LocalizedText(item.error.message, 'zh-CN'))
|
|
187
188
|
if (item.status === 'disabled') throw new Error('该插件仍被现有配置禁用,尚未生效')
|
|
@@ -40,9 +40,11 @@ import { readL4PluginReadme } from '@server/l4_foundation/pi/l4-pi-plugin-regist
|
|
|
40
40
|
import {
|
|
41
41
|
readL4PiPluginPreferences,
|
|
42
42
|
readL4PiPluginUpdateTag,
|
|
43
|
+
removeL4PiPluginPreference,
|
|
43
44
|
setL4PiPluginUpdateTag,
|
|
44
45
|
setL4PiPluginEnabled
|
|
45
46
|
} from '@server/l4_foundation/pi/l4-pi-plugin-preferences'
|
|
47
|
+
import { removeL4PiLocalExtension } from '@server/l4_foundation/pi/l4-pi-local-extension-maintenance'
|
|
46
48
|
import { getL4PiGlobalPluginRuntime } from '@server/l4_foundation/pi/l4-pi-global-plugin-runtime'
|
|
47
49
|
import {
|
|
48
50
|
L4PiPackageMaintenanceError,
|
|
@@ -109,6 +111,7 @@ interface L2PluginPreparedMutation {
|
|
|
109
111
|
request: L4PiDeskPackageMaintenanceRequest | null
|
|
110
112
|
updateTag: string | null
|
|
111
113
|
pluginName: string | null
|
|
114
|
+
localExtension?: boolean
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
export interface L2PluginManagementWorkSessions {
|
|
@@ -739,7 +742,12 @@ export class L2PluginManagement {
|
|
|
739
742
|
)
|
|
740
743
|
if (mutation !== 'add') {
|
|
741
744
|
if (!existing) throw new L2PluginManagementNotFoundError(source)
|
|
742
|
-
if (
|
|
745
|
+
if (
|
|
746
|
+
existing.kind !== 'package' &&
|
|
747
|
+
mutation !== 'del' &&
|
|
748
|
+
mutation !== 'enable' &&
|
|
749
|
+
mutation !== 'disable'
|
|
750
|
+
) {
|
|
743
751
|
throw new L2PluginManagementInvalidPackageError(
|
|
744
752
|
'裸扩展不是已安装包,请编辑源码后加载变更;此操作不会删除源码'
|
|
745
753
|
)
|
|
@@ -834,6 +842,18 @@ export class L2PluginManagement {
|
|
|
834
842
|
.listConfiguredPackages()
|
|
835
843
|
.filter((item) => item.scope === 'user')
|
|
836
844
|
const target = configured.find((item) => item.source === operationSource)
|
|
845
|
+
if (mutation === 'del' && !target) {
|
|
846
|
+
const existing = (await this.readSources()).sources.find(
|
|
847
|
+
(item) => item.source === operationSource
|
|
848
|
+
)
|
|
849
|
+
if (!existing) throw new L2PluginManagementNotFoundError(source)
|
|
850
|
+
return {
|
|
851
|
+
request: null,
|
|
852
|
+
updateTag: null,
|
|
853
|
+
pluginName: null,
|
|
854
|
+
...(existing.kind === 'extension' ? { localExtension: true } : {})
|
|
855
|
+
}
|
|
856
|
+
}
|
|
837
857
|
let request: L4PiDeskPackageMaintenanceRequest | null = null
|
|
838
858
|
let updateTag: string | null = null
|
|
839
859
|
let pluginName: string | null = null
|
|
@@ -876,7 +896,7 @@ export class L2PluginManagement {
|
|
|
876
896
|
prepared: L2PluginPreparedMutation
|
|
877
897
|
): Promise<void> {
|
|
878
898
|
const { mutation, source, operationSource } = input
|
|
879
|
-
const { request, updateTag, pluginName } = prepared
|
|
899
|
+
const { request, updateTag, pluginName, localExtension } = prepared
|
|
880
900
|
const runtime = getL4PiGlobalPluginRuntime()
|
|
881
901
|
const waiting = (reason: string): void => this.setOperation(operationSource, 'waiting', reason)
|
|
882
902
|
if (request) {
|
|
@@ -919,6 +939,26 @@ export class L2PluginManagement {
|
|
|
919
939
|
this.sourceCache = null
|
|
920
940
|
throw error
|
|
921
941
|
}
|
|
942
|
+
} else if (mutation === 'del' && !request) {
|
|
943
|
+
await runtime.replaceSource(operationSource, null)
|
|
944
|
+
if (localExtension) {
|
|
945
|
+
await removeL4PiLocalExtension({
|
|
946
|
+
source: operationSource,
|
|
947
|
+
agentDir: this.agentDir,
|
|
948
|
+
cwd: this.packageManagerCwd
|
|
949
|
+
})
|
|
950
|
+
} else {
|
|
951
|
+
removeL4PiPluginPreference(operationSource, this.agentDir)
|
|
952
|
+
}
|
|
953
|
+
for (const change of await runtime.scanSourceChanges([operationSource])) {
|
|
954
|
+
if (change.current) {
|
|
955
|
+
throw new L2PluginManagementInvalidPackageError(
|
|
956
|
+
'该来源仍被全局资源规则引用,请先移除对应规则后重试卸载'
|
|
957
|
+
)
|
|
958
|
+
}
|
|
959
|
+
await runtime.applySourceChange(change)
|
|
960
|
+
if (change.previous?.path) clearL4PiPluginNativeDiagnostics(change.previous.path)
|
|
961
|
+
}
|
|
922
962
|
} else if (request) {
|
|
923
963
|
try {
|
|
924
964
|
await runtime.replaceSource(operationSource, null)
|
|
@@ -1065,6 +1105,15 @@ export class L2PluginManagement {
|
|
|
1065
1105
|
DefaultPackageManager,
|
|
1066
1106
|
SettingsManager
|
|
1067
1107
|
})
|
|
1108
|
+
const current = new Set(snapshot.sources.map((item) => l2PluginSourceIdentity(item.source)))
|
|
1109
|
+
for (const previous of getL4PiGlobalPluginRuntime().readAppliedSources()) {
|
|
1110
|
+
if (!current.has(l2PluginSourceIdentity(previous.source))) {
|
|
1111
|
+
snapshot.sources.push({
|
|
1112
|
+
...previous,
|
|
1113
|
+
error: '插件来源已移除,运行资源尚未卸载'
|
|
1114
|
+
})
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1068
1117
|
if (!writing() || !this.sourceCache) this.sourceCache = snapshot
|
|
1069
1118
|
return writing() && this.sourceCache ? this.sourceCache : snapshot
|
|
1070
1119
|
}
|
|
@@ -9,7 +9,11 @@ import {
|
|
|
9
9
|
} from '@earendil-works/pi-coding-agent'
|
|
10
10
|
import { L4PiPluginOwnerRuntime, type L4PiPluginPackageSource } from './l4-pi-plugin-owner-runtime'
|
|
11
11
|
import { isL4PiDeskSafeMode } from './l4-pi-desk-mode'
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
discoverL4PiPluginSources,
|
|
14
|
+
type L4PiPluginSource,
|
|
15
|
+
type L4PiPluginSourceSnapshot
|
|
16
|
+
} from './l4-pi-plugin-sources'
|
|
13
17
|
import { L4PiPluginSourceChanges, type L4PiPluginSourceChange } from './l4-pi-plugin-source-changes'
|
|
14
18
|
import { clearL4PiPluginCodeCache } from './l4-pi-plugin-module-loader'
|
|
15
19
|
|
|
@@ -44,6 +48,10 @@ export class L4PiGlobalPluginRuntime extends L4PiPluginOwnerRuntime {
|
|
|
44
48
|
this.discover = discover
|
|
45
49
|
}
|
|
46
50
|
|
|
51
|
+
readAppliedSources(): L4PiPluginSource[] {
|
|
52
|
+
return this.sourceChanges.readAppliedSources()
|
|
53
|
+
}
|
|
54
|
+
|
|
47
55
|
async scanSourceChanges(sources?: readonly string[]): Promise<L4PiPluginSourceChange[]> {
|
|
48
56
|
await this.initialize()
|
|
49
57
|
return this.sourceChanges.scan(await this.discover(), sources)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import 'server-only'
|
|
2
|
+
|
|
3
|
+
import { readdir, realpath, rm, stat } from 'node:fs/promises'
|
|
4
|
+
import { homedir } from 'node:os'
|
|
5
|
+
import { join, resolve } from 'node:path'
|
|
6
|
+
import { SettingsManager } from '@earendil-works/pi-coding-agent'
|
|
7
|
+
import { l4PluginSourceIdentity } from '@common/l4_foundation/plugin/l4-plugin-package'
|
|
8
|
+
import { getL4FileSystemKey, isL4PathInsideRoot } from '@server/l4_foundation/file/l4-file-path'
|
|
9
|
+
import { readL4PiPluginPreferences, removeL4PiPluginPreference } from './l4-pi-plugin-preferences'
|
|
10
|
+
|
|
11
|
+
function isMissing(error: unknown): boolean {
|
|
12
|
+
return (
|
|
13
|
+
error !== null &&
|
|
14
|
+
typeof error === 'object' &&
|
|
15
|
+
'code' in error &&
|
|
16
|
+
(error.code === 'ENOENT' || error.code === 'ENOTDIR')
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function canonical(path: string): Promise<string> {
|
|
21
|
+
try {
|
|
22
|
+
return getL4FileSystemKey(await realpath(path))
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (!isMissing(error)) throw error
|
|
25
|
+
return getL4FileSystemKey(resolve(path))
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function removeL4PiLocalExtension(input: {
|
|
30
|
+
source: string
|
|
31
|
+
agentDir: string
|
|
32
|
+
cwd: string
|
|
33
|
+
}): Promise<void> {
|
|
34
|
+
const { source, agentDir, cwd } = input
|
|
35
|
+
try {
|
|
36
|
+
const settings = SettingsManager.create(cwd, agentDir, { projectTrusted: false })
|
|
37
|
+
const initialErrors = settings.drainErrors()
|
|
38
|
+
if (initialErrors.length) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`读取扩展配置失败:${initialErrors.map(({ error }) => error.message).join(';')}`
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const target = await canonical(source)
|
|
45
|
+
const metadata = await stat(source).catch((error: unknown) => {
|
|
46
|
+
if (!isMissing(error)) throw error
|
|
47
|
+
return null
|
|
48
|
+
})
|
|
49
|
+
const configured = settings.getGlobalSettings().extensions ?? []
|
|
50
|
+
const retained: string[] = []
|
|
51
|
+
for (const reference of configured) {
|
|
52
|
+
// 规则可能同时加载其他来源,留给卸载后的真实发现校验处理。
|
|
53
|
+
if (/^[!+-]|[*?{}[\]()]/.test(reference)) {
|
|
54
|
+
retained.push(reference)
|
|
55
|
+
continue
|
|
56
|
+
}
|
|
57
|
+
const path = await canonical(
|
|
58
|
+
reference.startsWith('~/')
|
|
59
|
+
? join(homedir(), reference.slice(2))
|
|
60
|
+
: resolve(agentDir, reference)
|
|
61
|
+
)
|
|
62
|
+
if (
|
|
63
|
+
path !== target &&
|
|
64
|
+
!((metadata === null || metadata.isDirectory()) && isL4PathInsideRoot(target, path))
|
|
65
|
+
) {
|
|
66
|
+
retained.push(reference)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const extensionDir = join(agentDir, 'extensions')
|
|
71
|
+
const entries = await readdir(extensionDir, { withFileTypes: true }).catch((error: unknown) => {
|
|
72
|
+
if (!isMissing(error)) throw error
|
|
73
|
+
return []
|
|
74
|
+
})
|
|
75
|
+
const removals: Array<{ path: string; recursive: boolean }> = []
|
|
76
|
+
for (const entry of entries) {
|
|
77
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules') continue
|
|
78
|
+
const path = join(extensionDir, entry.name)
|
|
79
|
+
if ((await canonical(path)) === target) {
|
|
80
|
+
removals.push({ path, recursive: !entry.isSymbolicLink() && entry.isDirectory() })
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const entry of removals) {
|
|
84
|
+
// 使用原始入口路径;链接只删入口,不能用规范目标执行递归删除。
|
|
85
|
+
await rm(entry.path, { recursive: entry.recursive, force: true })
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const removedReferences = configured.length - retained.length
|
|
89
|
+
if (removedReferences) {
|
|
90
|
+
settings.setExtensionPaths(retained)
|
|
91
|
+
await settings.flush()
|
|
92
|
+
const errors = settings.drainErrors()
|
|
93
|
+
if (errors.length) {
|
|
94
|
+
throw new Error(`保存扩展配置失败:${errors.map(({ error }) => error.message).join(';')}`)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const preferences = readL4PiPluginPreferences(agentDir)
|
|
99
|
+
const identity = l4PluginSourceIdentity(source)
|
|
100
|
+
const hasPreference =
|
|
101
|
+
preferences.disabled.includes(identity) ||
|
|
102
|
+
identity in preferences.registries ||
|
|
103
|
+
identity in preferences.updateTags
|
|
104
|
+
if (hasPreference) removeL4PiPluginPreference(source, agentDir)
|
|
105
|
+
|
|
106
|
+
if (removals.length || removedReferences || hasPreference) {
|
|
107
|
+
console.info('[Pi Desk][LocalExtension] 已移除本地扩展入口与加载声明', {
|
|
108
|
+
source,
|
|
109
|
+
entries: removals.length,
|
|
110
|
+
references: removedReferences
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
throw new Error(`移除本地扩展失败:${error instanceof Error ? error.message : String(error)}`, {
|
|
115
|
+
cause: error
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -437,7 +437,10 @@ export class L4PiPluginOwnerRuntime {
|
|
|
437
437
|
|
|
438
438
|
this.changingSources.add(source)
|
|
439
439
|
try {
|
|
440
|
-
if (owner)
|
|
440
|
+
if (owner) {
|
|
441
|
+
const released = this.releaseOwner(owner)
|
|
442
|
+
if (installedPath === null) await released
|
|
443
|
+
}
|
|
441
444
|
if (this.disposed) throw new Error('Global plugin runtime has been disposed')
|
|
442
445
|
clearL4PiPluginCodeCache()
|
|
443
446
|
this.packageDiagnostics.delete(source)
|
|
@@ -109,6 +109,10 @@ export class L4PiPluginSourceChanges {
|
|
|
109
109
|
for (const source of snapshot.sources) this.applied.set(source.source, await describe(source))
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
readAppliedSources(): L4PiPluginSource[] {
|
|
113
|
+
return structuredClone([...this.applied.values()].map((item) => item.source))
|
|
114
|
+
}
|
|
115
|
+
|
|
112
116
|
async scan(
|
|
113
117
|
snapshot: L4PiPluginSourceSnapshot,
|
|
114
118
|
requested?: readonly string[]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
ljXlpZIQpD6BvWyD8Fmc3
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
],
|
|
5
5
|
"devFiles": [],
|
|
6
6
|
"lowPriorityFiles": [
|
|
7
|
-
"static/
|
|
8
|
-
"static/
|
|
7
|
+
"static/ljXlpZIQpD6BvWyD8Fmc3/_buildManifest.js",
|
|
8
|
+
"static/ljXlpZIQpD6BvWyD8Fmc3/_ssgManifest.js"
|
|
9
9
|
],
|
|
10
10
|
"rootMainFiles": [
|
|
11
11
|
"static/chunks/webpack-a84310d111797bef.js",
|
|
@@ -2,8 +2,8 @@ self.__SERVER_FILES_MANIFEST={
|
|
|
2
2
|
"version": 1,
|
|
3
3
|
"config": {
|
|
4
4
|
"env": {
|
|
5
|
-
"PI_DESK_APP_VERSION": "1.1.
|
|
6
|
-
"PI_DESK_VERSION_UPDATED_AT": "2026-11-
|
|
5
|
+
"PI_DESK_APP_VERSION": "1.1.38-dev",
|
|
6
|
+
"PI_DESK_VERSION_UPDATED_AT": "2026-11-03T13:31:38+08:00"
|
|
7
7
|
},
|
|
8
8
|
"webpack": null,
|
|
9
9
|
"typescript": {
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"version": 1,
|
|
3
3
|
"config": {
|
|
4
4
|
"env": {
|
|
5
|
-
"PI_DESK_APP_VERSION": "1.1.
|
|
6
|
-
"PI_DESK_VERSION_UPDATED_AT": "2026-11-
|
|
5
|
+
"PI_DESK_APP_VERSION": "1.1.38-dev",
|
|
6
|
+
"PI_DESK_VERSION_UPDATED_AT": "2026-11-03T13:31:38+08:00"
|
|
7
7
|
},
|
|
8
8
|
"webpack": null,
|
|
9
9
|
"typescript": {
|