@talex-touch/utils 1.0.42 → 1.0.45
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/.eslintcache +1 -0
- package/__tests__/cloud-sync-sdk.test.ts +442 -0
- package/__tests__/icons/icons.test.ts +84 -0
- package/__tests__/plugin-sdk-lifecycle.test.ts +130 -0
- package/__tests__/power-sdk.test.ts +143 -0
- package/__tests__/preset-export-types.test.ts +108 -0
- package/__tests__/search/fuzzy-match.test.ts +137 -0
- package/__tests__/transport/port-policy.test.ts +44 -0
- package/__tests__/transport-domain-sdks.test.ts +152 -0
- package/__tests__/types/update.test.ts +67 -0
- package/account/account-sdk.ts +915 -0
- package/account/index.ts +2 -0
- package/account/types.ts +321 -0
- package/analytics/client.ts +136 -0
- package/analytics/index.ts +2 -0
- package/analytics/types.ts +156 -0
- package/animation/auto-resize.ts +322 -0
- package/animation/window-node.ts +26 -19
- package/auth/clerk-types.ts +12 -30
- package/auth/index.ts +0 -2
- package/auth/useAuthState.ts +6 -14
- package/base/index.ts +2 -0
- package/base/log-level.ts +105 -0
- package/channel/index.ts +170 -69
- package/cloud-sync/cloud-sync-sdk.ts +450 -0
- package/cloud-sync/index.ts +1 -0
- package/common/file-scan-utils.ts +17 -9
- package/common/index.ts +4 -0
- package/common/logger/index.ts +46 -0
- package/common/logger/logger-manager.ts +303 -0
- package/common/logger/module-logger.ts +270 -0
- package/common/logger/transport-logger.ts +234 -0
- package/common/logger/types.ts +93 -0
- package/common/search/gather.ts +48 -6
- package/common/search/index.ts +8 -0
- package/common/storage/constants.ts +13 -0
- package/common/storage/entity/app-settings.ts +245 -0
- package/common/storage/entity/index.ts +3 -0
- package/common/storage/entity/layout-atom-types.ts +147 -0
- package/common/storage/entity/openers.ts +1 -0
- package/common/storage/entity/preset-cloud-api.ts +132 -0
- package/common/storage/entity/preset-export-types.ts +256 -0
- package/common/storage/entity/shortcut-settings.ts +1 -0
- package/common/storage/shortcut-storage.ts +11 -0
- package/common/utils/clone-diagnostics.ts +105 -0
- package/common/utils/file.ts +16 -8
- package/common/utils/index.ts +6 -2
- package/common/utils/payload-preview.ts +173 -0
- package/common/utils/polling.ts +167 -13
- package/common/utils/safe-path.ts +103 -0
- package/common/utils/safe-shell.ts +115 -0
- package/common/utils/task-queue.ts +4 -1
- package/core-box/builder/tuff-builder.ts +0 -1
- package/core-box/index.ts +1 -1
- package/core-box/recommendation.ts +38 -1
- package/core-box/tuff/tuff-dsl.ts +32 -0
- package/electron/download-manager.ts +10 -7
- package/electron/env-tool.ts +42 -40
- package/electron/index.ts +0 -1
- package/env/index.ts +156 -0
- package/eslint.config.js +55 -0
- package/i18n/index.ts +62 -0
- package/i18n/locales/en.json +226 -0
- package/i18n/locales/zh.json +226 -0
- package/i18n/message-keys.ts +236 -0
- package/i18n/resolver.ts +181 -0
- package/icons/index.ts +257 -0
- package/icons/svg.ts +69 -0
- package/index.ts +9 -1
- package/intelligence/client.ts +72 -42
- package/market/constants.ts +9 -5
- package/market/index.ts +1 -1
- package/market/types.ts +19 -4
- package/package.json +15 -5
- package/permission/index.ts +143 -46
- package/permission/legacy.ts +26 -0
- package/permission/registry.ts +304 -0
- package/permission/types.ts +164 -0
- package/plugin/channel.ts +68 -39
- package/plugin/index.ts +80 -7
- package/plugin/install.ts +3 -0
- package/plugin/log/types.ts +22 -5
- package/plugin/node/logger-manager.ts +11 -3
- package/plugin/node/logger.ts +24 -17
- package/plugin/preload.ts +25 -2
- package/plugin/providers/index.ts +4 -4
- package/plugin/providers/market-client.ts +6 -3
- package/plugin/providers/npm-provider.ts +22 -7
- package/plugin/providers/tpex-provider.ts +22 -8
- package/plugin/sdk/box-items.ts +14 -0
- package/plugin/sdk/box-sdk.ts +64 -0
- package/plugin/sdk/channel.ts +119 -4
- package/plugin/sdk/clipboard.ts +26 -12
- package/plugin/sdk/cloud-sync.ts +113 -0
- package/plugin/sdk/common.ts +19 -11
- package/plugin/sdk/core-box.ts +6 -15
- package/plugin/sdk/division-box.ts +160 -65
- package/plugin/sdk/examples/storage-onDidChange-example.js +5 -2
- package/plugin/sdk/feature-sdk.ts +111 -76
- package/plugin/sdk/flow.ts +146 -45
- package/plugin/sdk/hooks/bridge.ts +13 -6
- package/plugin/sdk/hooks/life-cycle.ts +35 -16
- package/plugin/sdk/index.ts +14 -3
- package/plugin/sdk/intelligence.ts +87 -0
- package/plugin/sdk/meta/README.md +179 -0
- package/plugin/sdk/meta-sdk.ts +244 -0
- package/plugin/sdk/notification.ts +9 -0
- package/plugin/sdk/plugin-info.ts +64 -0
- package/plugin/sdk/power.ts +155 -0
- package/plugin/sdk/recommend.ts +21 -0
- package/plugin/sdk/service/index.ts +12 -8
- package/plugin/sdk/sqlite.ts +141 -0
- package/plugin/sdk/storage.ts +2 -6
- package/plugin/sdk/system.ts +2 -9
- package/plugin/sdk/temp-files.ts +41 -0
- package/plugin/sdk/touch-sdk.ts +18 -0
- package/plugin/sdk/types.ts +44 -4
- package/plugin/sdk/window/index.ts +12 -9
- package/plugin/sdk-version.ts +231 -0
- package/preload/renderer.ts +3 -2
- package/renderer/hooks/arg-mapper.ts +16 -2
- package/renderer/hooks/index.ts +13 -0
- package/renderer/hooks/initialize.ts +2 -1
- package/renderer/hooks/use-agent-market-sdk.ts +7 -0
- package/renderer/hooks/use-agent-market.ts +106 -0
- package/renderer/hooks/use-agents-sdk.ts +7 -0
- package/renderer/hooks/use-app-sdk.ts +7 -0
- package/renderer/hooks/use-channel.ts +33 -4
- package/renderer/hooks/use-download-sdk.ts +21 -0
- package/renderer/hooks/use-intelligence-sdk.ts +7 -0
- package/renderer/hooks/use-intelligence-stats.ts +290 -0
- package/renderer/hooks/use-intelligence.ts +55 -214
- package/renderer/hooks/use-market-sdk.ts +16 -0
- package/renderer/hooks/use-notification-sdk.ts +7 -0
- package/renderer/hooks/use-permission-sdk.ts +7 -0
- package/renderer/hooks/use-permission.ts +325 -0
- package/renderer/hooks/use-platform-sdk.ts +7 -0
- package/renderer/hooks/use-plugin-sdk.ts +16 -0
- package/renderer/hooks/use-settings-sdk.ts +7 -0
- package/renderer/hooks/use-update-sdk.ts +21 -0
- package/renderer/index.ts +1 -0
- package/renderer/ref.ts +19 -10
- package/renderer/shared/components/SharedPluginDetailContent.vue +84 -0
- package/renderer/shared/components/SharedPluginDetailHeader.vue +116 -0
- package/renderer/shared/components/SharedPluginDetailMetaList.vue +39 -0
- package/renderer/shared/components/SharedPluginDetailReadme.vue +45 -0
- package/renderer/shared/components/SharedPluginDetailVersions.vue +98 -0
- package/renderer/shared/components/index.ts +5 -0
- package/renderer/shared/components/shims-vue.d.ts +5 -0
- package/renderer/shared/index.ts +2 -0
- package/renderer/shared/plugin-detail.ts +62 -0
- package/renderer/storage/app-settings.ts +3 -1
- package/renderer/storage/base-storage.ts +508 -82
- package/renderer/storage/intelligence-storage.ts +31 -40
- package/renderer/storage/openers.ts +3 -1
- package/renderer/storage/storage-subscription.ts +126 -42
- package/renderer/touch-sdk/env.ts +10 -10
- package/renderer/touch-sdk/index.ts +114 -18
- package/renderer/touch-sdk/terminal.ts +24 -13
- package/search/feature-matcher.ts +279 -0
- package/search/fuzzy-match.ts +64 -34
- package/search/index.ts +10 -0
- package/search/levenshtein-utils.ts +17 -11
- package/transport/errors.ts +310 -0
- package/transport/event/builder.ts +378 -0
- package/transport/event/index.ts +7 -0
- package/transport/event/types.ts +292 -0
- package/transport/events/index.ts +2690 -0
- package/transport/events/meta-overlay.ts +79 -0
- package/transport/events/types/agents.ts +177 -0
- package/transport/events/types/app-index.ts +20 -0
- package/transport/events/types/app.ts +475 -0
- package/transport/events/types/box-item.ts +222 -0
- package/transport/events/types/clipboard.ts +80 -0
- package/transport/events/types/core-box.ts +534 -0
- package/transport/events/types/device-idle.ts +7 -0
- package/transport/events/types/division-box.ts +99 -0
- package/transport/events/types/download.ts +115 -0
- package/transport/events/types/file-index.ts +84 -0
- package/transport/events/types/flow.ts +149 -0
- package/transport/events/types/index.ts +70 -0
- package/transport/events/types/market.ts +39 -0
- package/transport/events/types/meta-overlay.ts +184 -0
- package/transport/events/types/notification.ts +140 -0
- package/transport/events/types/permission.ts +90 -0
- package/transport/events/types/platform.ts +8 -0
- package/transport/events/types/plugin.ts +631 -0
- package/transport/events/types/sentry.ts +20 -0
- package/transport/events/types/storage.ts +208 -0
- package/transport/events/types/transport.ts +60 -0
- package/transport/events/types/tray.ts +16 -0
- package/transport/events/types/update.ts +78 -0
- package/transport/index.ts +141 -0
- package/transport/main.ts +2 -0
- package/transport/prelude.ts +208 -0
- package/transport/sdk/constants.ts +29 -0
- package/transport/sdk/domains/agents-market.ts +47 -0
- package/transport/sdk/domains/agents.ts +62 -0
- package/transport/sdk/domains/app.ts +48 -0
- package/transport/sdk/domains/disposable.ts +35 -0
- package/transport/sdk/domains/download.ts +139 -0
- package/transport/sdk/domains/index.ts +13 -0
- package/transport/sdk/domains/intelligence.ts +616 -0
- package/transport/sdk/domains/market.ts +35 -0
- package/transport/sdk/domains/notification.ts +62 -0
- package/transport/sdk/domains/permission.ts +85 -0
- package/transport/sdk/domains/platform.ts +19 -0
- package/transport/sdk/domains/plugin.ts +144 -0
- package/transport/sdk/domains/settings.ts +102 -0
- package/transport/sdk/domains/update.ts +64 -0
- package/transport/sdk/index.ts +60 -0
- package/transport/sdk/main-transport.ts +710 -0
- package/transport/sdk/main.ts +9 -0
- package/transport/sdk/plugin-transport.ts +654 -0
- package/transport/sdk/port-policy.ts +38 -0
- package/transport/sdk/renderer-transport.ts +1165 -0
- package/transport/types.ts +605 -0
- package/types/agent.ts +399 -0
- package/types/cloud-sync.ts +157 -0
- package/types/division-box.ts +31 -31
- package/types/download.ts +1 -0
- package/types/flow.ts +63 -12
- package/types/icon.ts +2 -1
- package/types/index.ts +5 -0
- package/types/intelligence.ts +166 -173
- package/types/modules/base.ts +2 -0
- package/types/path-browserify.d.ts +5 -0
- package/types/platform.ts +12 -0
- package/types/startup-info.ts +32 -0
- package/types/touch-app-core.ts +8 -8
- package/types/update.ts +94 -1
- package/vitest.config.ts +25 -0
- package/auth/useClerkConfig.ts +0 -40
- package/auth/useClerkProvider.ts +0 -52
package/plugin/channel.ts
CHANGED
|
@@ -9,25 +9,45 @@ import {
|
|
|
9
9
|
ChannelType,
|
|
10
10
|
DataCode,
|
|
11
11
|
} from '../channel'
|
|
12
|
+
import { getLogger } from '../common/logger'
|
|
13
|
+
import { findCloneIssue, isCloneError, summarizeClonePayload } from '../common/utils/clone-diagnostics'
|
|
14
|
+
import { formatPayloadPreview } from '../common/utils/payload-preview'
|
|
15
|
+
import { hasWindow } from '../env'
|
|
12
16
|
|
|
13
|
-
const CHANNEL_DEFAULT_TIMEOUT =
|
|
17
|
+
const CHANNEL_DEFAULT_TIMEOUT = 60_000
|
|
14
18
|
|
|
15
19
|
let cachedIpcRenderer: IpcRenderer | null = null
|
|
20
|
+
const channelLog = getLogger('plugin-channel')
|
|
21
|
+
|
|
22
|
+
type PluginWindow = Window & {
|
|
23
|
+
$plugin?: { name?: string }
|
|
24
|
+
$channel?: ITouchClientChannel
|
|
25
|
+
electron?: { ipcRenderer?: IpcRenderer }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getPluginWindow(): PluginWindow | undefined {
|
|
29
|
+
return hasWindow() ? (window as unknown as PluginWindow) : undefined
|
|
30
|
+
}
|
|
16
31
|
|
|
17
32
|
// 使用惰性解析避免在打包阶段静态引入 electron
|
|
18
33
|
function resolveIpcRenderer(): IpcRenderer | null {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return bridge.ipcRenderer as IpcRenderer
|
|
23
|
-
}
|
|
34
|
+
const globalWindow = getPluginWindow()
|
|
35
|
+
if (globalWindow?.electron?.ipcRenderer)
|
|
36
|
+
return globalWindow.electron.ipcRenderer as IpcRenderer
|
|
24
37
|
|
|
25
38
|
try {
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
return
|
|
39
|
+
const electronFromGlobal = (globalThis as any)?.electron
|
|
40
|
+
if (electronFromGlobal?.ipcRenderer)
|
|
41
|
+
return electronFromGlobal.ipcRenderer as IpcRenderer
|
|
42
|
+
|
|
43
|
+
const requireFromGlobal = (globalThis as any)?.require
|
|
44
|
+
if (typeof requireFromGlobal === 'function') {
|
|
45
|
+
const electron = requireFromGlobal('electron')
|
|
46
|
+
if (electron?.ipcRenderer)
|
|
47
|
+
return electron.ipcRenderer as IpcRenderer
|
|
48
|
+
}
|
|
29
49
|
}
|
|
30
|
-
catch
|
|
50
|
+
catch {
|
|
31
51
|
// ignore – will throw below if no ipcRenderer is resolved
|
|
32
52
|
}
|
|
33
53
|
|
|
@@ -46,14 +66,17 @@ function ensureIpcRenderer(): IpcRenderer {
|
|
|
46
66
|
return cachedIpcRenderer
|
|
47
67
|
}
|
|
48
68
|
|
|
69
|
+
type ChannelListener = (data: StandardChannelData) => unknown
|
|
70
|
+
type PendingCallback = (data: RawStandardChannelData) => void
|
|
71
|
+
|
|
49
72
|
/**
|
|
50
73
|
* @deprecated This class is deprecated and will be removed in the future.
|
|
51
74
|
* Due to the new secret system, ipc message transmission should unique Key, and will inject when ui view attached.
|
|
52
75
|
*/
|
|
53
76
|
class TouchChannel implements ITouchClientChannel {
|
|
54
|
-
channelMap: Map<string,
|
|
77
|
+
channelMap: Map<string, ChannelListener[]> = new Map()
|
|
55
78
|
|
|
56
|
-
pendingMap: Map<string,
|
|
79
|
+
pendingMap: Map<string, PendingCallback> = new Map()
|
|
57
80
|
|
|
58
81
|
plugin: string
|
|
59
82
|
|
|
@@ -66,7 +89,7 @@ class TouchChannel implements ITouchClientChannel {
|
|
|
66
89
|
}
|
|
67
90
|
|
|
68
91
|
__parse_raw_data(e: IpcRendererEvent | undefined, arg: any): RawStandardChannelData | null {
|
|
69
|
-
|
|
92
|
+
channelLog.debug('Raw data', { meta: { payload: formatPayloadPreview(arg) } })
|
|
70
93
|
if (arg) {
|
|
71
94
|
const { name, header, code, data, sync } = arg
|
|
72
95
|
|
|
@@ -86,7 +109,7 @@ class TouchChannel implements ITouchClientChannel {
|
|
|
86
109
|
}
|
|
87
110
|
}
|
|
88
111
|
|
|
89
|
-
|
|
112
|
+
channelLog.error('Invalid message payload', { error: { event: e, payload: arg } })
|
|
90
113
|
return null
|
|
91
114
|
// throw new Error("Invalid message!");
|
|
92
115
|
}
|
|
@@ -154,7 +177,7 @@ class TouchChannel implements ITouchClientChannel {
|
|
|
154
177
|
|
|
155
178
|
regChannel(
|
|
156
179
|
eventName: string,
|
|
157
|
-
callback:
|
|
180
|
+
callback: ChannelListener,
|
|
158
181
|
): () => void {
|
|
159
182
|
const listeners = this.channelMap.get(eventName) || []
|
|
160
183
|
|
|
@@ -176,7 +199,7 @@ class TouchChannel implements ITouchClientChannel {
|
|
|
176
199
|
}
|
|
177
200
|
}
|
|
178
201
|
|
|
179
|
-
unRegChannel(eventName: string, callback:
|
|
202
|
+
unRegChannel(eventName: string, callback: ChannelListener): boolean {
|
|
180
203
|
const listeners = this.channelMap.get(eventName)
|
|
181
204
|
|
|
182
205
|
if (!listeners) {
|
|
@@ -200,16 +223,7 @@ class TouchChannel implements ITouchClientChannel {
|
|
|
200
223
|
}
|
|
201
224
|
|
|
202
225
|
private formatPayloadPreview(payload: unknown): string {
|
|
203
|
-
|
|
204
|
-
return String(payload)
|
|
205
|
-
if (typeof payload === 'string')
|
|
206
|
-
return payload.length > 200 ? `${payload.slice(0, 200)}…` : payload
|
|
207
|
-
try {
|
|
208
|
-
return JSON.stringify(payload)
|
|
209
|
-
}
|
|
210
|
-
catch {
|
|
211
|
-
return '[unserializable]'
|
|
212
|
-
}
|
|
226
|
+
return formatPayloadPreview(payload)
|
|
213
227
|
}
|
|
214
228
|
|
|
215
229
|
send(eventName: string, arg: any): Promise<any> {
|
|
@@ -239,13 +253,20 @@ class TouchChannel implements ITouchClientChannel {
|
|
|
239
253
|
}
|
|
240
254
|
catch (error) {
|
|
241
255
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
)
|
|
256
|
+
const meta: Record<string, unknown> = {
|
|
257
|
+
payloadPreview: this.formatPayloadPreview(arg)
|
|
258
|
+
}
|
|
259
|
+
if (isCloneError(error)) {
|
|
260
|
+
meta.cloneIssue = findCloneIssue(arg)
|
|
261
|
+
meta.payloadSummary = summarizeClonePayload(arg)
|
|
262
|
+
}
|
|
263
|
+
channelLog.error(`Failed to send \"${eventName}\": ${errorMessage}`, {
|
|
264
|
+
meta,
|
|
265
|
+
error,
|
|
266
|
+
})
|
|
246
267
|
reject(
|
|
247
268
|
Object.assign(
|
|
248
|
-
new Error(`Failed to send plugin channel message "${eventName}": ${errorMessage}`),
|
|
269
|
+
new Error(`Failed to send plugin channel message \"${eventName}\": ${errorMessage}`),
|
|
249
270
|
{ code: 'plugin_channel_send_failed' },
|
|
250
271
|
),
|
|
251
272
|
)
|
|
@@ -258,10 +279,10 @@ class TouchChannel implements ITouchClientChannel {
|
|
|
258
279
|
return
|
|
259
280
|
this.pendingMap.delete(uniqueId)
|
|
260
281
|
const timeoutError = Object.assign(
|
|
261
|
-
new Error(`Plugin channel request "${eventName}" timed out after ${timeoutMs}ms`),
|
|
282
|
+
new Error(`Plugin channel request \"${eventName}\" timed out after ${timeoutMs}ms`),
|
|
262
283
|
{ code: 'plugin_channel_timeout' },
|
|
263
284
|
)
|
|
264
|
-
|
|
285
|
+
channelLog.warn(timeoutError.message)
|
|
265
286
|
reject(timeoutError)
|
|
266
287
|
}, timeoutMs)
|
|
267
288
|
|
|
@@ -299,12 +320,19 @@ class TouchChannel implements ITouchClientChannel {
|
|
|
299
320
|
}
|
|
300
321
|
catch (error) {
|
|
301
322
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
302
|
-
|
|
323
|
+
const meta: Record<string, unknown> = {
|
|
303
324
|
eventName,
|
|
304
|
-
error: errorMessage,
|
|
305
325
|
payloadPreview: this.formatPayloadPreview(arg),
|
|
326
|
+
}
|
|
327
|
+
if (isCloneError(error)) {
|
|
328
|
+
meta.cloneIssue = findCloneIssue(arg)
|
|
329
|
+
meta.payloadSummary = summarizeClonePayload(arg)
|
|
330
|
+
}
|
|
331
|
+
channelLog.error('Failed to sendSync message', {
|
|
332
|
+
meta,
|
|
333
|
+
error,
|
|
306
334
|
})
|
|
307
|
-
throw new Error(`Failed to sendSync plugin channel message "${eventName}": ${errorMessage}`)
|
|
335
|
+
throw new Error(`Failed to sendSync plugin channel message \"${eventName}\": ${errorMessage}`)
|
|
308
336
|
}
|
|
309
337
|
}
|
|
310
338
|
}
|
|
@@ -313,13 +341,14 @@ let touchChannel: ITouchClientChannel | null = null
|
|
|
313
341
|
|
|
314
342
|
export function genChannel(): ITouchClientChannel {
|
|
315
343
|
if (!touchChannel) {
|
|
316
|
-
|
|
344
|
+
const globalWindow = getPluginWindow()
|
|
345
|
+
const pluginName = globalWindow?.$plugin?.name
|
|
346
|
+
if (!globalWindow || !pluginName) {
|
|
317
347
|
throw new Error('TouchChannel cannot be initialized outside plugin renderer context')
|
|
318
348
|
}
|
|
319
349
|
|
|
320
|
-
const pluginName = (window as any).$plugin.name as string
|
|
321
350
|
touchChannel = new TouchChannel(pluginName)
|
|
322
|
-
|
|
351
|
+
globalWindow.$channel = touchChannel
|
|
323
352
|
}
|
|
324
353
|
|
|
325
354
|
return touchChannel
|
package/plugin/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { Arch, SupportOS } from './../base/index'
|
|
|
4
4
|
|
|
5
5
|
import type { IPluginLogger } from './log/types'
|
|
6
6
|
|
|
7
|
-
import type { PluginInstallRequest, PluginInstallSummary } from './providers'
|
|
7
|
+
import type { PluginInstallRequest, PluginInstallSummary } from './providers/types'
|
|
8
8
|
|
|
9
9
|
export enum PluginStatus {
|
|
10
10
|
DISABLED,
|
|
@@ -33,6 +33,14 @@ export interface PluginIssue {
|
|
|
33
33
|
timestamp?: number
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
export interface PluginMeta {
|
|
37
|
+
/**
|
|
38
|
+
* Internal plugins are created in code (no manifest / scanning).
|
|
39
|
+
* They should be hidden in UI unless developer mode is enabled.
|
|
40
|
+
*/
|
|
41
|
+
internal?: boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
36
44
|
export interface DevServerHealthCheckResult {
|
|
37
45
|
healthy: boolean
|
|
38
46
|
version?: string
|
|
@@ -68,13 +76,44 @@ export interface IPluginDev {
|
|
|
68
76
|
source?: boolean
|
|
69
77
|
}
|
|
70
78
|
|
|
79
|
+
/**
|
|
80
|
+
* SDK API version for plugin compatibility checking.
|
|
81
|
+
* Format: YYMMDD (e.g., 251212 = 2025-12-12)
|
|
82
|
+
*
|
|
83
|
+
* Rules:
|
|
84
|
+
* - Not declared or < PERMISSION_ENFORCEMENT_MIN_VERSION: legacy mode (permissions bypassed)
|
|
85
|
+
* - >= PERMISSION_ENFORCEMENT_MIN_VERSION: permissions enforced
|
|
86
|
+
*/
|
|
87
|
+
export type SdkApiVersion = number
|
|
88
|
+
|
|
71
89
|
export interface ITouchPlugin extends IPluginBaseInfo {
|
|
72
90
|
dev: IPluginDev
|
|
73
91
|
pluginPath: string
|
|
74
92
|
logger: IPluginLogger<any>
|
|
93
|
+
/**
|
|
94
|
+
* Category id synced with Nexus (e.g., 'utilities', 'productivity').
|
|
95
|
+
* Used for UI grouping and marketplace filtering.
|
|
96
|
+
*/
|
|
97
|
+
category?: string
|
|
98
|
+
meta?: PluginMeta
|
|
75
99
|
features: IPluginFeature[]
|
|
76
100
|
issues: PluginIssue[]
|
|
77
101
|
divisionBoxConfig?: import('../types/division-box').ManifestDivisionBoxConfig
|
|
102
|
+
/**
|
|
103
|
+
* SDK API version declared by the plugin.
|
|
104
|
+
* Used for compatibility checking and permission enforcement.
|
|
105
|
+
* Format: YYMMDD (e.g., 251212)
|
|
106
|
+
*/
|
|
107
|
+
sdkapi?: SdkApiVersion
|
|
108
|
+
/**
|
|
109
|
+
* Declared permissions from manifest.
|
|
110
|
+
* Used for permission checking and UI display.
|
|
111
|
+
*/
|
|
112
|
+
declaredPermissions?: {
|
|
113
|
+
required: string[]
|
|
114
|
+
optional: string[]
|
|
115
|
+
reasons: Record<string, string>
|
|
116
|
+
}
|
|
78
117
|
|
|
79
118
|
addFeature: (feature: IPluginFeature) => boolean
|
|
80
119
|
delFeature: (featureId: string) => boolean
|
|
@@ -102,14 +141,21 @@ export interface ITouchPlugin extends IPluginBaseInfo {
|
|
|
102
141
|
* @param content The content of the file.
|
|
103
142
|
* @returns The result of the save operation.
|
|
104
143
|
*/
|
|
105
|
-
savePluginFile: (
|
|
144
|
+
savePluginFile: (
|
|
145
|
+
fileName: string,
|
|
146
|
+
content: object,
|
|
147
|
+
options?: { broadcast?: boolean }
|
|
148
|
+
) => { success: boolean, error?: string }
|
|
106
149
|
|
|
107
150
|
/**
|
|
108
151
|
* Delete the plugin file.
|
|
109
152
|
* @param fileName The name of the file.
|
|
110
153
|
* @returns The result of the delete operation.
|
|
111
154
|
*/
|
|
112
|
-
deletePluginFile: (
|
|
155
|
+
deletePluginFile: (
|
|
156
|
+
fileName: string,
|
|
157
|
+
options?: { broadcast?: boolean }
|
|
158
|
+
) => { success: boolean, error?: string }
|
|
113
159
|
|
|
114
160
|
/**
|
|
115
161
|
* List all files in the plugin.
|
|
@@ -133,11 +179,13 @@ export interface ITouchPlugin extends IPluginBaseInfo {
|
|
|
133
179
|
|
|
134
180
|
export interface IFeatureCommand {
|
|
135
181
|
type: 'match' | 'contain' | 'regex' | 'function' | 'over' | 'image' | 'files' | 'directory' | 'window'
|
|
136
|
-
value: string | string[] | RegExp |
|
|
182
|
+
value: string | string[] | RegExp | FeatureCommandMatcher
|
|
137
183
|
/** Optional trigger callback - not serialized over IPC */
|
|
138
184
|
onTrigger?: () => void
|
|
139
185
|
}
|
|
140
186
|
|
|
187
|
+
export type FeatureCommandMatcher = (queryText: string) => boolean
|
|
188
|
+
|
|
141
189
|
export interface IPluginFeature {
|
|
142
190
|
id: string
|
|
143
191
|
name: string
|
|
@@ -148,6 +196,10 @@ export interface IPluginFeature {
|
|
|
148
196
|
platform: IPlatform
|
|
149
197
|
commands: IFeatureCommand[]
|
|
150
198
|
interaction?: IFeatureInteraction
|
|
199
|
+
/**
|
|
200
|
+
* Experimental features are hidden unless the plugin runs in dev mode.
|
|
201
|
+
*/
|
|
202
|
+
experimental?: boolean
|
|
151
203
|
/**
|
|
152
204
|
* Internal search tokens generated at runtime for better matching
|
|
153
205
|
*/
|
|
@@ -175,6 +227,17 @@ export interface IFeatureInteraction {
|
|
|
175
227
|
* The relative path to the html file from the plugin root.
|
|
176
228
|
*/
|
|
177
229
|
path?: string
|
|
230
|
+
/**
|
|
231
|
+
* Whether to show the input field in CoreBox when this feature is active.
|
|
232
|
+
* Defaults to true for webcontent type.
|
|
233
|
+
*/
|
|
234
|
+
showInput?: boolean
|
|
235
|
+
/**
|
|
236
|
+
* Whether to automatically enable input monitoring for this feature.
|
|
237
|
+
* If true, plugin will receive input change events without calling allowInput().
|
|
238
|
+
* Defaults to true for webcontent features.
|
|
239
|
+
*/
|
|
240
|
+
allowInput?: boolean
|
|
178
241
|
}
|
|
179
242
|
|
|
180
243
|
/**
|
|
@@ -391,6 +454,16 @@ export interface IManifest {
|
|
|
391
454
|
* Version of the plugin, following semantic versioning (e.g., "1.0.0").
|
|
392
455
|
*/
|
|
393
456
|
version: string
|
|
457
|
+
/**
|
|
458
|
+
* SDK API version for compatibility checking.
|
|
459
|
+
* Format: YYMMDD (e.g., 251212 = 2025-12-12)
|
|
460
|
+
* Plugins without this field or with version < 251212 will bypass permission enforcement.
|
|
461
|
+
*/
|
|
462
|
+
sdkapi?: SdkApiVersion
|
|
463
|
+
/**
|
|
464
|
+
* Category id synced with Nexus (e.g., 'utilities', 'productivity').
|
|
465
|
+
*/
|
|
466
|
+
category?: string
|
|
394
467
|
/**
|
|
395
468
|
* Short description of the plugin's functionality.
|
|
396
469
|
*/
|
|
@@ -500,8 +573,8 @@ export interface IManifest {
|
|
|
500
573
|
}
|
|
501
574
|
|
|
502
575
|
export * from './install'
|
|
503
|
-
export type { IPluginLogger, LogDataType, LogItem
|
|
504
|
-
export * from './providers'
|
|
576
|
+
export type { IPluginLogger, LogDataType, LogItem } from './log/types'
|
|
505
577
|
export * from './risk'
|
|
506
|
-
export * from './sdk
|
|
578
|
+
export * from './sdk-version'
|
|
579
|
+
// Plugin runtime SDK should be imported from `@talex-touch/utils/plugin/sdk` to avoid root export collisions.
|
|
507
580
|
export * from './widget'
|
package/plugin/install.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type PluginInstallTaskStage
|
|
2
2
|
= | 'queued'
|
|
3
3
|
| 'downloading'
|
|
4
|
+
| 'verifying'
|
|
4
5
|
| 'awaiting-confirmation'
|
|
5
6
|
| 'installing'
|
|
6
7
|
| 'completed'
|
|
@@ -20,6 +21,8 @@ export interface PluginInstallProgressEvent {
|
|
|
20
21
|
/** 插件唯一标识或名称(由客户端提供)。 */
|
|
21
22
|
pluginId?: string
|
|
22
23
|
pluginName?: string
|
|
24
|
+
/** 来源提供者 ID,用于区分不同市场源的同名插件。 */
|
|
25
|
+
providerId?: string
|
|
23
26
|
/** 队列中的剩余任务数量(包含当前任务)。 */
|
|
24
27
|
remaining?: number
|
|
25
28
|
/** 当前任务在队列中的位置(0 表示正在处理)。 */
|
package/plugin/log/types.ts
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
|
+
import type { LogLevelString as BaseLogLevelString } from '../../base/log-level'
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
+
* Re-export unified LogLevel from base
|
|
3
4
|
*/
|
|
4
|
-
|
|
5
|
+
import {
|
|
6
|
+
LogLevel as BaseLogLevel,
|
|
7
|
+
|
|
8
|
+
logLevelToString as baseLogLevelToString,
|
|
9
|
+
stringToLogLevel as baseStringToLogLevel,
|
|
10
|
+
} from '../../base/log-level'
|
|
11
|
+
|
|
12
|
+
export const LogLevel = BaseLogLevel
|
|
13
|
+
export type LogLevelString = BaseLogLevelString
|
|
14
|
+
export const logLevelToString = baseLogLevelToString
|
|
15
|
+
export const stringToLogLevel = baseStringToLogLevel
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Legacy string-based log level type (for backward compatibility)
|
|
19
|
+
* @deprecated Use LogLevel enum instead
|
|
20
|
+
*/
|
|
21
|
+
export type LogLevelLegacy = 'INFO' | 'WARN' | 'ERROR' | 'DEBUG'
|
|
5
22
|
|
|
6
23
|
/**
|
|
7
24
|
* Supported data types for logging arguments.
|
|
@@ -14,8 +31,8 @@ export type LogDataType = string | number | boolean | object
|
|
|
14
31
|
export interface LogItem {
|
|
15
32
|
/** ISO timestamp when the log was created */
|
|
16
33
|
timestamp: string
|
|
17
|
-
/** Logging severity level */
|
|
18
|
-
level:
|
|
34
|
+
/** Logging severity level (uppercase string for storage compatibility) */
|
|
35
|
+
level: LogLevelString
|
|
19
36
|
/** Plugin name */
|
|
20
37
|
plugin: string
|
|
21
38
|
/** Main log message */
|
|
@@ -27,7 +44,7 @@ export interface LogItem {
|
|
|
27
44
|
}
|
|
28
45
|
|
|
29
46
|
/**
|
|
30
|
-
* Minimal contract for plugin loggers
|
|
47
|
+
* Minimal contract for plugin loggers
|
|
31
48
|
*/
|
|
32
49
|
export interface IPluginLogger<TManager = unknown> {
|
|
33
50
|
info: (...args: LogDataType[]) => void
|
|
@@ -3,6 +3,7 @@ import type { LogItem } from '../log/types'
|
|
|
3
3
|
import fs from 'node:fs'
|
|
4
4
|
import path from 'node:path'
|
|
5
5
|
import { structuredStrictStringify } from '@talex-touch/utils'
|
|
6
|
+
import { PollingService } from '../../common/utils/polling'
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* PluginLoggerManager is responsible for managing and writing logs for a specific plugin.
|
|
@@ -14,7 +15,8 @@ export class PluginLoggerManager {
|
|
|
14
15
|
private readonly pluginInfoPath: string
|
|
15
16
|
private readonly sessionStart: string
|
|
16
17
|
private buffer: LogItem[] = []
|
|
17
|
-
private
|
|
18
|
+
private readonly pollingService = PollingService.getInstance()
|
|
19
|
+
private readonly flushTaskId: string
|
|
18
20
|
private onLogAppend?: (log: LogItem) => void
|
|
19
21
|
|
|
20
22
|
/**
|
|
@@ -33,9 +35,15 @@ export class PluginLoggerManager {
|
|
|
33
35
|
this.pluginLogDir = path.resolve(baseDir, 'logs', sessionFolder)
|
|
34
36
|
this.sessionLogPath = path.resolve(this.pluginLogDir, 'session.log')
|
|
35
37
|
this.pluginInfoPath = path.resolve(this.pluginLogDir, 'touch-plugin.info')
|
|
38
|
+
this.flushTaskId = `plugin-logger.flush.${pluginInfo.name}.${Date.now()}`
|
|
36
39
|
|
|
37
40
|
this.ensureLogEnvironment(true)
|
|
38
|
-
this.
|
|
41
|
+
this.pollingService.register(
|
|
42
|
+
this.flushTaskId,
|
|
43
|
+
() => this.flush(),
|
|
44
|
+
{ interval: 5, unit: 'seconds' },
|
|
45
|
+
)
|
|
46
|
+
this.pollingService.start()
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
/**
|
|
@@ -89,7 +97,7 @@ export class PluginLoggerManager {
|
|
|
89
97
|
* Stops the flush interval and ensures remaining logs are written.
|
|
90
98
|
*/
|
|
91
99
|
destroy(): void {
|
|
92
|
-
|
|
100
|
+
this.pollingService.unregister(this.flushTaskId)
|
|
93
101
|
this.flush()
|
|
94
102
|
}
|
|
95
103
|
|
package/plugin/node/logger.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
-
import type { IPluginLogger, LogDataType, LogItem,
|
|
1
|
+
import type { IPluginLogger, LogDataType, LogItem, LogLevelString } from '../log/types'
|
|
2
2
|
import type { PluginLoggerManager } from './logger-manager'
|
|
3
|
+
import process from 'node:process'
|
|
4
|
+
import { inspect } from 'node:util'
|
|
3
5
|
import chalk from 'chalk'
|
|
4
6
|
|
|
7
|
+
const pluginLogStdout =
|
|
8
|
+
process.env.TALEX_PLUGIN_LOG_STDOUT === '1' || process.env.TALEX_PLUGIN_LOG_STDOUT === 'true'
|
|
9
|
+
const defaultStdoutLevels = new Set<LogLevelString>(['WARN', 'ERROR'])
|
|
10
|
+
|
|
11
|
+
function shouldWriteToStdout(level: LogLevelString): boolean {
|
|
12
|
+
if (pluginLogStdout) return true
|
|
13
|
+
return defaultStdoutLevels.has(level)
|
|
14
|
+
}
|
|
15
|
+
|
|
5
16
|
/**
|
|
6
17
|
* PluginLogger provides structured logging capabilities for individual plugins.
|
|
7
18
|
*/
|
|
@@ -61,13 +72,13 @@ export class PluginLogger implements IPluginLogger<PluginLoggerManager> {
|
|
|
61
72
|
* @param level - The severity level of the log.
|
|
62
73
|
* @param args - The log message and optional data payload.
|
|
63
74
|
*/
|
|
64
|
-
private log(level:
|
|
75
|
+
private log(level: LogLevelString, ...args: LogDataType[]): void {
|
|
65
76
|
const [message, ...data] = args
|
|
66
77
|
|
|
67
78
|
const normalizedLevel = (typeof level === 'string' ? level.toUpperCase() : level) as string
|
|
68
|
-
const allowedLevels:
|
|
69
|
-
const resolvedLevel = (allowedLevels.includes(normalizedLevel as
|
|
70
|
-
? (normalizedLevel as
|
|
79
|
+
const allowedLevels: LogLevelString[] = ['INFO', 'WARN', 'ERROR', 'DEBUG']
|
|
80
|
+
const resolvedLevel = (allowedLevels.includes(normalizedLevel as LogLevelString)
|
|
81
|
+
? (normalizedLevel as LogLevelString)
|
|
71
82
|
: 'INFO')
|
|
72
83
|
if (resolvedLevel === 'INFO' && normalizedLevel !== 'INFO') {
|
|
73
84
|
console.warn(
|
|
@@ -75,11 +86,12 @@ export class PluginLogger implements IPluginLogger<PluginLoggerManager> {
|
|
|
75
86
|
)
|
|
76
87
|
}
|
|
77
88
|
|
|
78
|
-
const levelColorMap: Record<
|
|
89
|
+
const levelColorMap: Record<LogLevelString, (input: string) => string> = {
|
|
79
90
|
INFO: chalk.bgBlue,
|
|
80
91
|
WARN: chalk.bgYellow,
|
|
81
92
|
ERROR: chalk.bgRed,
|
|
82
93
|
DEBUG: chalk.bgGray,
|
|
94
|
+
NONE: chalk.bgBlack,
|
|
83
95
|
}
|
|
84
96
|
const colorize = levelColorMap[resolvedLevel] ?? ((input: string) => input)
|
|
85
97
|
|
|
@@ -93,17 +105,12 @@ export class PluginLogger implements IPluginLogger<PluginLoggerManager> {
|
|
|
93
105
|
}
|
|
94
106
|
this.manager.append(log)
|
|
95
107
|
|
|
96
|
-
if (resolvedLevel
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
else {
|
|
103
|
-
console.log(
|
|
104
|
-
`${chalk.bgMagenta('[PluginLog]')} ${colorize(resolvedLevel)} ${this.pluginName} - ${message}`,
|
|
105
|
-
...data,
|
|
106
|
-
)
|
|
108
|
+
if (shouldWriteToStdout(resolvedLevel)) {
|
|
109
|
+
const baseMessage = `${chalk.bgMagenta('[PluginLog]')} ${colorize(resolvedLevel)} ${this.pluginName} - ${message}`
|
|
110
|
+
const extra = data.length
|
|
111
|
+
? ` ${data.map(item => (typeof item === 'string' ? item : inspect(item))).join(' ')}`
|
|
112
|
+
: ''
|
|
113
|
+
process.stdout.write(`${baseMessage}${extra}\n`)
|
|
107
114
|
}
|
|
108
115
|
}
|
|
109
116
|
}
|
package/plugin/preload.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { ITouchClientChannel } from '../channel'
|
|
2
|
+
import type { ITuffTransport } from '../transport'
|
|
2
3
|
import type { ITouchSDK } from './sdk/index'
|
|
4
|
+
import { getLogger } from '../common/logger'
|
|
5
|
+
import { createPluginTuffTransport } from '../transport'
|
|
6
|
+
import { defineRawEvent } from '../transport/event/builder'
|
|
3
7
|
// Import SDK for side effects (initializes hooks)
|
|
4
8
|
import './sdk/index'
|
|
5
9
|
|
|
@@ -9,8 +13,11 @@ declare global {
|
|
|
9
13
|
$plugin: {
|
|
10
14
|
name: string
|
|
11
15
|
path: object
|
|
16
|
+
version?: string
|
|
17
|
+
sdkapi?: number
|
|
12
18
|
}
|
|
13
19
|
$channel: ITouchClientChannel
|
|
20
|
+
$transport?: ITuffTransport
|
|
14
21
|
$crash: (message: string, extraData: any) => void
|
|
15
22
|
$config: {
|
|
16
23
|
themeStyle: any
|
|
@@ -19,14 +26,30 @@ declare global {
|
|
|
19
26
|
}
|
|
20
27
|
}
|
|
21
28
|
|
|
29
|
+
const preloadLog = getLogger('plugin-preload')
|
|
30
|
+
const crashEvent = defineRawEvent<Record<string, string | number | boolean | undefined>, void>('crash')
|
|
31
|
+
|
|
22
32
|
export function initTuff(window: Window) {
|
|
23
33
|
const plugin = window.$plugin
|
|
24
34
|
if (!plugin)
|
|
25
35
|
throw new Error('Plugin has a fatal error! Please check your plugin!')
|
|
26
36
|
|
|
37
|
+
if (!window.$transport && window.$channel) {
|
|
38
|
+
window.$transport = createPluginTuffTransport(window.$channel)
|
|
39
|
+
}
|
|
40
|
+
if (window.$transport) {
|
|
41
|
+
window.addEventListener?.('beforeunload', () => {
|
|
42
|
+
window.$transport?.destroy()
|
|
43
|
+
}, { once: true })
|
|
44
|
+
}
|
|
45
|
+
|
|
27
46
|
window.$crash = function (message, extraData) {
|
|
28
|
-
window.$
|
|
47
|
+
if (window.$transport) {
|
|
48
|
+
void window.$transport.send(crashEvent, { message, ...extraData })
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
window.$channel?.send?.('crash', { message, ...extraData })
|
|
29
52
|
}
|
|
30
53
|
|
|
31
|
-
|
|
54
|
+
preloadLog.info(`[Plugin] ${plugin.name} loaded`)
|
|
32
55
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
export * from './market-client'
|
|
2
|
+
export * from './npm-provider'
|
|
1
3
|
export * from './registry'
|
|
2
|
-
export * from './types'
|
|
3
|
-
export * from './tpex-types'
|
|
4
4
|
export * from './tpex-provider'
|
|
5
|
-
export * from './
|
|
6
|
-
export * from './
|
|
5
|
+
export * from './tpex-types'
|
|
6
|
+
export * from './types'
|
|
@@ -133,7 +133,8 @@ export class PluginMarketClient {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
const sorted = results.sort((a, b) => {
|
|
136
|
-
if (a.isOfficial !== b.isOfficial)
|
|
136
|
+
if (a.isOfficial !== b.isOfficial)
|
|
137
|
+
return a.isOfficial ? -1 : 1
|
|
137
138
|
return (b.downloads ?? 0) - (a.downloads ?? 0)
|
|
138
139
|
})
|
|
139
140
|
|
|
@@ -156,7 +157,8 @@ export class PluginMarketClient {
|
|
|
156
157
|
if (source === 'tpex' || (!source && !identifier.includes('/'))) {
|
|
157
158
|
try {
|
|
158
159
|
const plugin = await this.tpexProvider.getPlugin(identifier)
|
|
159
|
-
if (plugin)
|
|
160
|
+
if (plugin)
|
|
161
|
+
return normalizeTpexPlugin(plugin)
|
|
160
162
|
}
|
|
161
163
|
catch {
|
|
162
164
|
// Fall through to npm
|
|
@@ -166,7 +168,8 @@ export class PluginMarketClient {
|
|
|
166
168
|
if (source === 'npm' || !source) {
|
|
167
169
|
try {
|
|
168
170
|
const pkg = await this.npmProvider.getPackageInfo(identifier)
|
|
169
|
-
if (pkg)
|
|
171
|
+
if (pkg)
|
|
172
|
+
return normalizeNpmPlugin(pkg)
|
|
170
173
|
}
|
|
171
174
|
catch {
|
|
172
175
|
// Not found
|