@motrix/plugin-cli 2.1.0 → 2.2.0

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.
@@ -9,9 +9,9 @@
9
9
  "dev": "motrix-plugin dev"
10
10
  },
11
11
  "devDependencies": {
12
- "@motrix/plugin-api": "^2.0.0",
13
- "@motrix/plugin-cli": "^2.0.0",
14
- "esbuild": "^0.24",
15
- "typescript": "^5.6"
12
+ "@motrix/plugin-api": "^2.1.0",
13
+ "@motrix/plugin-cli": "^2.1.1",
14
+ "esbuild": "^0.28.2",
15
+ "typescript": "^7.0.2"
16
16
  }
17
17
  }
@@ -0,0 +1,8 @@
1
+ node_modules/
2
+ dist/
3
+ *.tsbuildinfo
4
+ *.log
5
+ .env
6
+ .env.*
7
+ !.env.example
8
+ .DS_Store
@@ -0,0 +1,94 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ **English** | [简体中文](./README.zh-CN.md)
4
+
5
+ A text deduplication operation scaffolded for `{{PLUGIN_ID}}`.
6
+
7
+ > **Unreleased protocol and template.** Use a Motrix development build and SDK
8
+ > packages that implement operations v1, composable UI and scoped text files.
9
+ > The host range is `>=2.0.0-beta.48 <3.0.0`, targeting beta.48 as the first
10
+ > supporting release. Beta.47 is rejected, including development builds still
11
+ > reporting that version. This target and the dependency ranges do not establish
12
+ > published support. Raise the host floor if these features ship after beta.48.
13
+
14
+ ## Set up matching development dependencies
15
+
16
+ Build a matching plugin-sdk checkout first with `pnpm install --frozen-lockfile`
17
+ and `pnpm build` in that checkout. If this project was created immediately inside
18
+ that checkout's root, run the following **from this project directory**:
19
+
20
+ ```sh
21
+ pnpm add -D @motrix/plugin-api@file:../packages/plugin-api @motrix/plugin-cli@file:../packages/plugin-cli
22
+ ```
23
+
24
+ If the project lives elsewhere, adjust both `file:` paths to the matching built
25
+ SDK packages. Do not replace this step with a plain install of the older published
26
+ API/CLI. The source checkout's schema is embedded in its CLI build. When matching
27
+ SDK packages are released, replace these local dependencies with their actual
28
+ released versions; no minimum published SDK package version is assigned here.
29
+
30
+ ## Check and pack
31
+
32
+ ```sh
33
+ pnpm run typecheck
34
+ pnpm run validate
35
+ pnpm run pack
36
+ pnpm run lint
37
+ ```
38
+
39
+ `pack` bundles `src/index.ts` and writes the unsigned
40
+ `dist/{{PLUGIN_ID}}-0.1.0.moext`. `lint` checks the built bundle, so it runs after
41
+ `pack`. `pnpm run build` is an optional bundle-only step. No command here signs or
42
+ publishes. Install the archive through a compatible host's normal third-party
43
+ installation flow and review the two selected-file permissions.
44
+
45
+ ## Files to edit
46
+
47
+ | File | Role |
48
+ |---|---|
49
+ | `motrix-plugin.json` | Private operation command, argument/result schemas, fields, layout, files, entries and repeat policy |
50
+ | `src/index.ts` | Typed sandbox handler, cancellation checks, progress and scoped file calls |
51
+ | `locales/en-US.json`, `locales/zh-CN.json` | Complete labels, hints, actions and result titles |
52
+ | `tsconfig.json` | Strict checking through the installed `@motrix/plugin-api`; no SDK checkout path is embedded |
53
+ | `esbuild.config.mjs` | Standalone ES2020 bundle with `motrix:plugin-api` kept external |
54
+
55
+ The handler and manifest use the same `{{PLUGIN_ID}}.deduplicate` identity.
56
+ Changing an argument means updating its schema, handler type, field and layout
57
+ reference together. Persistent preferences belong in plugin configuration, not
58
+ operation arguments. The current template does not generate types from schemas.
59
+
60
+ ## Behavior and limits
61
+
62
+ Select one UTF-8 `.txt` file, optionally enable **Ignore case**, and select a new
63
+ output. The handler preserves the first occurrence and spelling of each LF-split
64
+ line; case-insensitive keys use `toLowerCase()`. It does not trim whitespace or
65
+ normalize line endings. Empty segments, including a trailing newline or an empty
66
+ file, count as lines; CRLF preserves `\r`. The host displays the unique line count.
67
+
68
+ The form is available in the plugin page and for one completed single-file task.
69
+ **Process another copy** preserves options and reauthorizes unchanged input,
70
+ clearing output. Locale changes preserve form values. Electron selects native
71
+ files; Web selects files on the server, not browser-local uploads.
72
+
73
+ File strings are opaque single-invocation references, never paths. Declaring
74
+ permissions does not grant arbitrary file access. The host limits text input and
75
+ output to 1 MiB, prohibits output overwrite, checks authorization again during
76
+ execution and handles progress, cancellation, temporary files and publication.
77
+ The template requests no FFmpeg, network or unrestricted filesystem capability.
78
+
79
+ ## Try it
80
+
81
+ - For `Apple\napple\nBanana\nApple` without a trailing newline, expect 3 unique
82
+ lines with Ignore case off and 2 with it on.
83
+ - Check English/Chinese labels, the collapsible options group, both entry points
84
+ and repeated execution with a fresh output.
85
+ - An existing destination, stale/changed input or unauthorized reference must be
86
+ rejected. Never remove host authorization checks to make an example run.
87
+ - Cancel before output publication and wait for cleanup. A tiny input may finish
88
+ too quickly for manual cancellation; that is not cancellation test evidence.
89
+
90
+ For current controls, layout/condition rules, file grants, limits and unsupported
91
+ features, read the [SDK operation guide](https://github.com/motrixapp/plugin-sdk/blob/main/docs/operations.md).
92
+ Before these sources are published, read `docs/operations.md` in the matching SDK
93
+ checkout instead. New UI nodes/capabilities need host support; the template does
94
+ not provide custom renderer HTML, JavaScript or CSS.
@@ -0,0 +1,57 @@
1
+ # {{PROJECT_NAME}}
2
+
3
+ [English](./README.md) | **简体中文**
4
+
5
+ 为 `{{PLUGIN_ID}}` 生成的文本去重操作项目。
6
+
7
+ > **协议与模板尚未发布。** 使用包含操作 v1、可组合 UI 和受限文本文件能力的 Motrix 构建及 SDK 包。宿主范围为 `>=2.0.0-beta.48 <3.0.0`,以 beta.48 为首个支持版本目标。beta.47 会被拒绝,包括仍使用该版本号的开发构建。此目标与依赖版本范围不代表已发布支持;若功能晚于 beta.48 发布,须同步提高最低宿主版本。
8
+
9
+ ## 配置匹配的开发依赖
10
+
11
+ 先在匹配的 plugin-sdk 源码仓库执行 `pnpm install --frozen-lockfile` 和 `pnpm build`。如果本项目直接创建在该仓库根目录下,请在**本项目目录**执行:
12
+
13
+ ```sh
14
+ pnpm add -D @motrix/plugin-api@file:../packages/plugin-api @motrix/plugin-cli@file:../packages/plugin-cli
15
+ ```
16
+
17
+ 如果项目位于其他位置,将两个 `file:` 路径改为实际构建好的 SDK 包位置。不能用普通安装旧版 npm API/CLI 来代替这一步;源码中的 Schema 会内嵌到 CLI 构建产物中。配套 SDK 正式发布后,再将本地依赖改为实际发布版本;模板目前不指定已发布 SDK 包的最低版本。
18
+
19
+ ## 检查与打包
20
+
21
+ ```sh
22
+ pnpm run typecheck
23
+ pnpm run validate
24
+ pnpm run pack
25
+ pnpm run lint
26
+ ```
27
+
28
+ `pack` 打包 `src/index.ts`,输出未签名的 `dist/{{PLUGIN_ID}}-0.1.0.moext`。`lint` 检查 bundle,因此在 pack 后执行。`pnpm run build` 是可选的单独构建 bundle 步骤。这些命令不签名或发布。通过兼容宿主的正常第三方安装流程安装归档,并检查两项所选文件权限。
29
+
30
+ ## 需要编辑的文件
31
+
32
+ | 文件 | 作用 |
33
+ |---|---|
34
+ | `motrix-plugin.json` | 私有操作命令、参数和结果 Schema、字段、布局、文件、入口、再次执行策略 |
35
+ | `src/index.ts` | 带类型的沙箱处理函数、取消检查、进度和受限文件调用 |
36
+ | `locales/en-US.json`、`locales/zh-CN.json` | 完整标签、说明、按钮和结果标题 |
37
+ | `tsconfig.json` | 通过安装的 `@motrix/plugin-api` 严格检查,不内嵌 SDK 仓库路径 |
38
+ | `esbuild.config.mjs` | 独立 ES2020 bundle,保持 `motrix:plugin-api` 为 external |
39
+
40
+ 处理函数和清单使用相同的 `{{PLUGIN_ID}}.deduplicate` 身份。修改参数时,同时调整 Schema、处理函数类型、字段和布局引用。长期偏好属于插件配置,不属于操作参数;当前模板不从 Schema 自动生成类型。
41
+
42
+ ## 行为与限制
43
+
44
+ 选择一个 UTF-8 `.txt` 文件,按需启用“忽略大小写”,再选择新的输出。处理函数按 LF 分行,保留每行首次出现的位置和原文,忽略大小写时使用 `toLowerCase()` 比较键。不裁剪空白、不归一化换行;空片段也计为一行,包括末尾换行和空文件产生的片段,CRLF 保留 `\r`。宿主显示去重后的行数。
45
+
46
+ 插件页面及一个已完成的单文件任务菜单均提供入口。“再次处理”保留选项、重新授权未变化输入并清空输出。语言切换保留表单值。Electron 选择原生文件;Web 选择服务器上的文件,不上传浏览器本机文件。
47
+
48
+ 文件字符串是不透明单次调用引用,不是路径;声明权限不授予任意文件访问。宿主将文本输入、输出各限制为 1 MiB,禁止覆盖目标,执行时重新检查授权,并管理进度、取消、临时文件和最终保存。模板不申请 FFmpeg、网络或任意文件系统能力。
49
+
50
+ ## 验证方法
51
+
52
+ - 使用没有末尾换行的 `Apple\napple\nBanana\nApple`,关闭忽略大小写时应得到 3 行,开启时应得到 2 行。
53
+ - 检查中英文标签、可折叠选项组、两个入口,以及用新输出再次执行。
54
+ - 已有目标、过期或变化的输入、未经授权引用应被拒绝;不能移除宿主授权检查来运行示例。
55
+ - 在保存前取消并等待清理。小文件可能快到无法手工取消,不能将其算作取消验证证据。
56
+
57
+ 控件、布局及条件、文件授权、限制和未支持能力见 [SDK 操作指南](https://github.com/motrixapp/plugin-sdk/blob/main/docs/operations.zh-CN.md)。源码发布前,请改为阅读配套 SDK 仓库中的 `docs/operations.zh-CN.md`。新增 UI 节点或能力需要宿主支持;模板不提供自定义 renderer HTML、JavaScript 或 CSS。
@@ -0,0 +1,13 @@
1
+ import { build } from 'esbuild'
2
+
3
+ await build({
4
+ entryPoints: ['src/index.ts'],
5
+ bundle: true,
6
+ format: 'esm',
7
+ target: 'es2020',
8
+ platform: 'neutral',
9
+ outfile: 'dist/plugin.js',
10
+ external: ['motrix:plugin-api'],
11
+ minify: true,
12
+ treeShaking: true,
13
+ })
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "description": "Remove duplicate lines from a selected text file and save a new copy.",
4
+ "deduplicate": "Deduplicate lines",
5
+ "operationDescription": "Remove duplicate lines while keeping their original order, then save a new UTF-8 file.",
6
+ "input": "Input file",
7
+ "inputPlaceholder": "Choose a text file",
8
+ "inputBrowse": "Choose input file",
9
+ "output": "Output file",
10
+ "outputPlaceholder": "Choose a save location",
11
+ "outputBrowse": "Choose output file",
12
+ "ignoreCase": "Ignore case",
13
+ "ignoreCaseHelp": "Treat Apple and apple as the same line.",
14
+ "matchingOptions": "Matching options",
15
+ "uniqueLines": "Unique lines",
16
+ "submit": "Deduplicate",
17
+ "repeat": "Process another copy",
18
+ "footer": "Original text stays unchanged."
19
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "description": "去除所选文本文件中的重复行,并另存为新文件。",
4
+ "deduplicate": "文本去重",
5
+ "operationDescription": "去除重复行,保留原有顺序,并另存为 UTF-8 文本文件。",
6
+ "input": "输入文件",
7
+ "inputPlaceholder": "选择文本文件",
8
+ "inputBrowse": "选择输入文件",
9
+ "output": "输出文件",
10
+ "outputPlaceholder": "选择保存位置",
11
+ "outputBrowse": "选择输出文件",
12
+ "ignoreCase": "忽略大小写",
13
+ "ignoreCaseHelp": "将 Apple 和 apple 视为同一行。",
14
+ "matchingOptions": "匹配选项",
15
+ "uniqueLines": "去重后行数",
16
+ "submit": "去重",
17
+ "repeat": "再次处理",
18
+ "footer": "保留原始文本文件。"
19
+ }
@@ -0,0 +1,181 @@
1
+ {
2
+ "manifestVersion": 1,
3
+ "id": "{{PLUGIN_ID}}",
4
+ "name": "%name%",
5
+ "version": "0.1.0",
6
+ "description": "%description%",
7
+ "categories": ["post-action"],
8
+ "engines": {
9
+ "motrix": ">=2.0.0-beta.48 <3.0.0"
10
+ },
11
+ "main": "dist/plugin.js",
12
+ "permissions": ["files.selected.read", "files.selected.write"],
13
+ "activationEvents": ["onCommand:{{PLUGIN_ID}}.deduplicate"],
14
+ "contributes": {
15
+ "commands": [
16
+ {
17
+ "id": "{{PLUGIN_ID}}.deduplicate",
18
+ "title": "%deduplicate%",
19
+ "execution": "operation",
20
+ "public": false,
21
+ "argsSchema": {
22
+ "type": "object",
23
+ "properties": {
24
+ "input": {
25
+ "type": "string",
26
+ "minLength": 36,
27
+ "maxLength": 36
28
+ },
29
+ "output": {
30
+ "type": "string",
31
+ "minLength": 36,
32
+ "maxLength": 36
33
+ },
34
+ "ignoreCase": {
35
+ "type": "boolean",
36
+ "default": false
37
+ }
38
+ },
39
+ "required": ["input", "output", "ignoreCase"],
40
+ "additionalProperties": false
41
+ },
42
+ "resultSchema": {
43
+ "type": "object",
44
+ "properties": {
45
+ "output": {
46
+ "type": "string",
47
+ "minLength": 36,
48
+ "maxLength": 36
49
+ },
50
+ "lines": {
51
+ "type": "integer",
52
+ "title": "%uniqueLines%",
53
+ "minimum": 0,
54
+ "maximum": 1048576
55
+ }
56
+ },
57
+ "required": ["output", "lines"],
58
+ "additionalProperties": false
59
+ }
60
+ }
61
+ ],
62
+ "operations": {
63
+ "version": 1,
64
+ "items": [
65
+ {
66
+ "id": "{{PLUGIN_ID}}.deduplicate",
67
+ "title": "%deduplicate%",
68
+ "description": "%operationDescription%",
69
+ "icon": "document",
70
+ "command": "{{PLUGIN_ID}}.deduplicate",
71
+ "requires": ["operations.v1", "operations.ui.v1", "files.text.v1"],
72
+ "fields": [
73
+ {
74
+ "field": "input",
75
+ "label": "%input%",
76
+ "appearance": {
77
+ "icon": "document",
78
+ "tone": "blue",
79
+ "drop": true,
80
+ "placeholder": "%inputPlaceholder%",
81
+ "browseLabel": "%inputBrowse%"
82
+ }
83
+ },
84
+ {
85
+ "field": "ignoreCase",
86
+ "label": "%ignoreCase%",
87
+ "widget": "switch",
88
+ "help": "%ignoreCaseHelp%"
89
+ },
90
+ {
91
+ "field": "output",
92
+ "label": "%output%",
93
+ "appearance": {
94
+ "icon": "folder",
95
+ "tone": "amber",
96
+ "placeholder": "%outputPlaceholder%",
97
+ "browseLabel": "%outputBrowse%"
98
+ }
99
+ }
100
+ ],
101
+ "files": {
102
+ "input": {
103
+ "kind": "input-file",
104
+ "use": "text",
105
+ "extensions": ["txt"]
106
+ },
107
+ "output": {
108
+ "kind": "output-file",
109
+ "use": "text",
110
+ "extensions": ["txt"],
111
+ "suggestedName": {
112
+ "stemFrom": "input",
113
+ "suffix": "-unique"
114
+ }
115
+ }
116
+ },
117
+ "entries": [
118
+ {
119
+ "surface": "plugin-detail"
120
+ },
121
+ {
122
+ "surface": "task-menu",
123
+ "selection": {
124
+ "count": {
125
+ "min": 1,
126
+ "max": 1
127
+ },
128
+ "every": {
129
+ "status": "completed",
130
+ "files": "single",
131
+ "local": true
132
+ }
133
+ },
134
+ "bind": {
135
+ "input": {
136
+ "source": "task-file",
137
+ "index": 0
138
+ }
139
+ }
140
+ }
141
+ ],
142
+ "result": {
143
+ "fields": ["lines"],
144
+ "files": ["output"]
145
+ },
146
+ "ui": {
147
+ "layout": {
148
+ "type": "stack",
149
+ "children": [
150
+ {
151
+ "type": "field",
152
+ "field": "input"
153
+ },
154
+ {
155
+ "type": "group",
156
+ "title": "%matchingOptions%",
157
+ "children": [
158
+ {
159
+ "type": "field",
160
+ "field": "ignoreCase"
161
+ }
162
+ ],
163
+ "collapsible": true
164
+ },
165
+ {
166
+ "type": "field",
167
+ "field": "output"
168
+ }
169
+ ]
170
+ },
171
+ "submitLabel": "%submit%",
172
+ "repeatLabel": "%repeat%",
173
+ "footer": "%footer%",
174
+ "repeat": "preserve-inputs"
175
+ }
176
+ }
177
+ ]
178
+ }
179
+ },
180
+ "l10n": "locales"
181
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "private": true,
6
+ "engines": {
7
+ "node": ">=20"
8
+ },
9
+ "scripts": {
10
+ "build": "node esbuild.config.mjs",
11
+ "typecheck": "tsc --noEmit",
12
+ "validate": "motrix-plugin validate",
13
+ "pack": "motrix-plugin pack",
14
+ "lint": "motrix-plugin lint",
15
+ "dev": "motrix-plugin dev"
16
+ },
17
+ "devDependencies": {
18
+ "@motrix/plugin-api": "^2.2.0",
19
+ "@motrix/plugin-cli": "^2.2.0",
20
+ "esbuild": "^0.28.2",
21
+ "typescript": "^7.0.2"
22
+ }
23
+ }
@@ -0,0 +1,19 @@
1
+ import { commands } from 'motrix:plugin-api'
2
+
3
+ commands.registerOperation<
4
+ { input: string; output: string; ignoreCase: boolean },
5
+ { output: string; lines: number }
6
+ >('{{PLUGIN_ID}}.deduplicate', async (args, context) => {
7
+ const text = await context.files.readText(args.input)
8
+ const seen = new Set<string>()
9
+ const lines = text.split('\n').filter((line) => {
10
+ context.signal.throwIfAborted()
11
+ const key = args.ignoreCase ? line.toLowerCase() : line
12
+ if (seen.has(key)) return false
13
+ seen.add(key)
14
+ return true
15
+ })
16
+ await context.progress({ phase: 'running', fraction: 0.5 })
17
+ await context.files.writeText(args.output, lines.join('\n'))
18
+ return { output: args.output, lines: lines.length }
19
+ })
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "lib": ["ES2022", "DOM"],
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "types": ["@motrix/plugin-api"]
12
+ },
13
+ "include": ["src"]
14
+ }
@@ -9,9 +9,9 @@
9
9
  "dev": "motrix-plugin dev"
10
10
  },
11
11
  "devDependencies": {
12
- "@motrix/plugin-api": "^2.0.0",
13
- "@motrix/plugin-cli": "^2.0.0",
14
- "esbuild": "^0.24",
15
- "typescript": "^5.6"
12
+ "@motrix/plugin-api": "^2.1.0",
13
+ "@motrix/plugin-cli": "^2.1.1",
14
+ "esbuild": "^0.28.2",
15
+ "typescript": "^7.0.2"
16
16
  }
17
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motrix/plugin-cli",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "motrix-plugin": "dist/bin/motrix-plugin.js",
@@ -18,23 +18,23 @@
18
18
  "directory": "packages/plugin-cli"
19
19
  },
20
20
  "license": "MIT",
21
- "scripts": {
22
- "build": "tsup",
23
- "typecheck": "tsc --noEmit",
24
- "test": "vitest run"
25
- },
26
21
  "dependencies": {
27
- "chokidar": "^4",
28
- "commander": "^12",
29
- "esbuild": "^0.24",
30
- "yazl": "^2"
22
+ "chokidar": "^5.0.0",
23
+ "commander": "^15.0.0",
24
+ "esbuild": "^0.28.2",
25
+ "yazl": "^3.3.1"
31
26
  },
32
27
  "devDependencies": {
33
- "@motrix/plugin-manifest-schema": "workspace:*",
28
+ "@motrix/plugin-manifest-schema": "2.2.0",
34
29
  "@types/node": "^24",
35
- "tsup": "^8",
36
- "typescript": "^5.6",
37
- "vitest": "^3",
38
- "zod": "^4"
30
+ "tsup": "^8.5.1",
31
+ "typescript": "^7.0.2",
32
+ "vitest": "^4.1.11",
33
+ "zod": "^4.5.4"
34
+ },
35
+ "scripts": {
36
+ "build": "tsup",
37
+ "typecheck": "tsc --noEmit",
38
+ "test": "vitest run"
39
39
  }
40
- }
40
+ }