@shgroup/opencode-serenity-plugin 0.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.
- package/README.md +199 -0
- package/bin/opencode-serenity-plugin.js +316 -0
- package/dist/activation.d.ts +40 -0
- package/dist/activation.d.ts.map +1 -0
- package/dist/activation.js +133 -0
- package/dist/activation.js.map +1 -0
- package/dist/bash-override.d.ts +10 -0
- package/dist/bash-override.d.ts.map +1 -0
- package/dist/bash-override.js +24 -0
- package/dist/bash-override.js.map +1 -0
- package/dist/bash-toggle.d.ts +17 -0
- package/dist/bash-toggle.d.ts.map +1 -0
- package/dist/bash-toggle.js +42 -0
- package/dist/bash-toggle.js.map +1 -0
- package/dist/config-schema.d.ts +300 -0
- package/dist/config-schema.d.ts.map +1 -0
- package/dist/config-schema.js +185 -0
- package/dist/config-schema.js.map +1 -0
- package/dist/errors.d.ts +90 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +151 -0
- package/dist/errors.js.map +1 -0
- package/dist/fs/file-system-tool.d.ts +25 -0
- package/dist/fs/file-system-tool.d.ts.map +1 -0
- package/dist/fs/file-system-tool.js +318 -0
- package/dist/fs/file-system-tool.js.map +1 -0
- package/dist/fs/resolve-path.d.ts +53 -0
- package/dist/fs/resolve-path.d.ts.map +1 -0
- package/dist/fs/resolve-path.js +100 -0
- package/dist/fs/resolve-path.js.map +1 -0
- package/dist/hooks/compacting.d.ts +23 -0
- package/dist/hooks/compacting.d.ts.map +1 -0
- package/dist/hooks/compacting.js +90 -0
- package/dist/hooks/compacting.js.map +1 -0
- package/dist/hooks/permission-auto-reply.d.ts +91 -0
- package/dist/hooks/permission-auto-reply.d.ts.map +1 -0
- package/dist/hooks/permission-auto-reply.js +158 -0
- package/dist/hooks/permission-auto-reply.js.map +1 -0
- package/dist/hooks/permission-guards.d.ts +41 -0
- package/dist/hooks/permission-guards.d.ts.map +1 -0
- package/dist/hooks/permission-guards.js +153 -0
- package/dist/hooks/permission-guards.js.map +1 -0
- package/dist/hooks/shell-env.d.ts +20 -0
- package/dist/hooks/shell-env.d.ts.map +1 -0
- package/dist/hooks/shell-env.js +38 -0
- package/dist/hooks/shell-env.js.map +1 -0
- package/dist/hooks/util.d.ts +81 -0
- package/dist/hooks/util.d.ts.map +1 -0
- package/dist/hooks/util.js +172 -0
- package/dist/hooks/util.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/init/init-wizard.d.ts +39 -0
- package/dist/init/init-wizard.d.ts.map +1 -0
- package/dist/init/init-wizard.js +297 -0
- package/dist/init/init-wizard.js.map +1 -0
- package/dist/install.d.ts +117 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +255 -0
- package/dist/install.js.map +1 -0
- package/dist/msm-schema.d.ts +76 -0
- package/dist/msm-schema.d.ts.map +1 -0
- package/dist/msm-schema.js +207 -0
- package/dist/msm-schema.js.map +1 -0
- package/dist/msm.d.ts +25 -0
- package/dist/msm.d.ts.map +1 -0
- package/dist/msm.js +317 -0
- package/dist/msm.js.map +1 -0
- package/dist/session/lib.d.ts +33 -0
- package/dist/session/lib.d.ts.map +1 -0
- package/dist/session/lib.js +475 -0
- package/dist/session/lib.js.map +1 -0
- package/dist/session/session-tool.d.ts +17 -0
- package/dist/session/session-tool.d.ts.map +1 -0
- package/dist/session/session-tool.js +109 -0
- package/dist/session/session-tool.js.map +1 -0
- package/dist/skills/install-skill.d.ts +36 -0
- package/dist/skills/install-skill.d.ts.map +1 -0
- package/dist/skills/install-skill.js +91 -0
- package/dist/skills/install-skill.js.map +1 -0
- package/dist/skills/template-loader.d.ts +79 -0
- package/dist/skills/template-loader.d.ts.map +1 -0
- package/dist/skills/template-loader.js +170 -0
- package/dist/skills/template-loader.js.map +1 -0
- package/dist/state.d.ts +35 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +62 -0
- package/dist/state.js.map +1 -0
- package/dist/tui.d.ts +61 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/tui.js +279 -0
- package/dist/tui.js.map +1 -0
- package/dist/types/index.d.ts +30 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +17 -0
- package/dist/types/index.js.map +1 -0
- package/dist/util/config-patch.d.ts +58 -0
- package/dist/util/config-patch.d.ts.map +1 -0
- package/dist/util/config-patch.js +117 -0
- package/dist/util/config-patch.js.map +1 -0
- package/dist/util/git.d.ts +29 -0
- package/dist/util/git.d.ts.map +1 -0
- package/dist/util/git.js +74 -0
- package/dist/util/git.js.map +1 -0
- package/dist/util/init-check.d.ts +22 -0
- package/dist/util/init-check.d.ts.map +1 -0
- package/dist/util/init-check.js +76 -0
- package/dist/util/init-check.js.map +1 -0
- package/dist/util/init.d.ts +54 -0
- package/dist/util/init.d.ts.map +1 -0
- package/dist/util/init.js +87 -0
- package/dist/util/init.js.map +1 -0
- package/dist/util/log.d.ts +25 -0
- package/dist/util/log.d.ts.map +1 -0
- package/dist/util/log.js +28 -0
- package/dist/util/log.js.map +1 -0
- package/dist/util/msm-call.d.ts +48 -0
- package/dist/util/msm-call.d.ts.map +1 -0
- package/dist/util/msm-call.js +86 -0
- package/dist/util/msm-call.js.map +1 -0
- package/dist/util/msm-exec-runtime.d.ts +123 -0
- package/dist/util/msm-exec-runtime.d.ts.map +1 -0
- package/dist/util/msm-exec-runtime.js +532 -0
- package/dist/util/msm-exec-runtime.js.map +1 -0
- package/dist/util/path.d.ts +10 -0
- package/dist/util/path.d.ts.map +1 -0
- package/dist/util/path.js +21 -0
- package/dist/util/path.js.map +1 -0
- package/dist/util/ready-state.d.ts +43 -0
- package/dist/util/ready-state.d.ts.map +1 -0
- package/dist/util/ready-state.js +104 -0
- package/dist/util/ready-state.js.map +1 -0
- package/dist/util/serenity-file.d.ts +30 -0
- package/dist/util/serenity-file.d.ts.map +1 -0
- package/dist/util/serenity-file.js +69 -0
- package/dist/util/serenity-file.js.map +1 -0
- package/dist/util/tui-install.d.ts +61 -0
- package/dist/util/tui-install.d.ts.map +1 -0
- package/dist/util/tui-install.js +94 -0
- package/dist/util/tui-install.js.map +1 -0
- package/docs/architecture-v0.md +294 -0
- package/docs/contract-v0.md +417 -0
- package/docs/plugin-self-contained-msm-v1.md +182 -0
- package/docs/refactor-direction-v1.11.md +78 -0
- package/docs/requirements-v0-scope.md +104 -0
- package/docs/requirements-v0-summary.md +108 -0
- package/docs/rr7-init-design.md +304 -0
- package/docs/v0.1-candidates.md +132 -0
- package/package.json +54 -0
package/dist/install.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v1.11 — bin install CLI lib (pure logic, no side effects at import)
|
|
3
|
+
*
|
|
4
|
+
* 职责:
|
|
5
|
+
* - 检测 global config home (XDG_CONFIG_HOME / ~/.config / %APPDATA%)
|
|
6
|
+
* - 解析 install path → 两个 plugin entry (server + tui)
|
|
7
|
+
* - 读写 JSON config (atomic write via tmp + rename)
|
|
8
|
+
* - 幂等: 同一 plugin (id 或 abs path) 不重复添加
|
|
9
|
+
* - 追踪 _plugin_origins: 知道每个 entry 是哪个 plugin 装的、何时装、从哪装
|
|
10
|
+
*
|
|
11
|
+
* 与 src/util/tui-install.ts 的关系:
|
|
12
|
+
* - tui-install.ts 只管 global tui.json,只放 dist/tui.js,函数风格 imperative
|
|
13
|
+
* - install.ts 通用化: 既管 tui.json 也管 opencode.json,同时写多个 entry
|
|
14
|
+
* - 复用 tui-install.ts 的 toPluginSpec 思想 (realpath + file://)
|
|
15
|
+
*
|
|
16
|
+
* D23 (2026-06-07): 两 entry 架构不可破 — server entry 仅 project-level
|
|
17
|
+
* 加载,TUI entry 全局加载,保留 V2 非侵入语义。
|
|
18
|
+
*/
|
|
19
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, renameSync, realpathSync, } from 'node:fs';
|
|
20
|
+
import { dirname, join, isAbsolute } from 'node:path';
|
|
21
|
+
import { homedir, platform } from 'node:os';
|
|
22
|
+
import { pathToFileURL } from 'node:url';
|
|
23
|
+
export const PLUGIN_ID = 'opencode-serenity-plugin';
|
|
24
|
+
const DIST_DIR = 'dist';
|
|
25
|
+
const SERVER_ENTRY_FILENAME = 'index.js';
|
|
26
|
+
const TUI_ENTRY_FILENAME = 'tui.js';
|
|
27
|
+
const OPENCODE_SCHEMA_URL = 'https://opencode.ai/config.json';
|
|
28
|
+
const TUI_SCHEMA_URL = 'https://opencode.ai/tui.json';
|
|
29
|
+
// ── 路径解析 ──
|
|
30
|
+
/**
|
|
31
|
+
* 解析 global opencode config 目录。
|
|
32
|
+
*
|
|
33
|
+
* 优先级:
|
|
34
|
+
* 1. $XDG_CONFIG_HOME/opencode (XDG Base Directory 规范,Linux/macOS)
|
|
35
|
+
* 2. $APPDATA/opencode (Windows 优先)
|
|
36
|
+
* 3. ~/AppData/Roaming/opencode (Windows 兜底)
|
|
37
|
+
* 4. ~/.config/opencode (Unix 兜底)
|
|
38
|
+
*/
|
|
39
|
+
export function detectGlobalConfigHome() {
|
|
40
|
+
const xdg = process.env['XDG_CONFIG_HOME'];
|
|
41
|
+
if (typeof xdg === 'string' && xdg.trim().length > 0) {
|
|
42
|
+
return join(xdg, 'opencode');
|
|
43
|
+
}
|
|
44
|
+
if (platform() === 'win32') {
|
|
45
|
+
const appdata = process.env['APPDATA'];
|
|
46
|
+
if (typeof appdata === 'string' && appdata.trim().length > 0) {
|
|
47
|
+
return join(appdata, 'opencode');
|
|
48
|
+
}
|
|
49
|
+
return join(homedir(), 'AppData', 'Roaming', 'opencode');
|
|
50
|
+
}
|
|
51
|
+
return join(homedir(), '.config', 'opencode');
|
|
52
|
+
}
|
|
53
|
+
/** global config 文件路径 */
|
|
54
|
+
export function getGlobalConfigPath(filename) {
|
|
55
|
+
return join(detectGlobalConfigHome(), filename);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 解析 install path → 两个 plugin entry。
|
|
59
|
+
*
|
|
60
|
+
* - installPath 应为 package 根目录 (含 dist/index.js + dist/tui.js)
|
|
61
|
+
* - 自动 realpath 解析 symlink (npm global install / pnpm link 都会建 symlink)
|
|
62
|
+
* - 绝对路径验证 (相对路径直接抛)
|
|
63
|
+
*/
|
|
64
|
+
export function resolvePluginEntries(installPath) {
|
|
65
|
+
if (!isAbsolute(installPath)) {
|
|
66
|
+
throw new Error(`installPath must be absolute: ${installPath}`);
|
|
67
|
+
}
|
|
68
|
+
const realInstall = realpathSync(installPath);
|
|
69
|
+
const serverAbs = join(realInstall, DIST_DIR, SERVER_ENTRY_FILENAME);
|
|
70
|
+
const tuiAbs = join(realInstall, DIST_DIR, TUI_ENTRY_FILENAME);
|
|
71
|
+
return {
|
|
72
|
+
id: PLUGIN_ID,
|
|
73
|
+
server: {
|
|
74
|
+
id: PLUGIN_ID,
|
|
75
|
+
path: pathToFileURL(serverAbs).href,
|
|
76
|
+
absPath: serverAbs,
|
|
77
|
+
},
|
|
78
|
+
tui: {
|
|
79
|
+
id: PLUGIN_ID,
|
|
80
|
+
path: pathToFileURL(tuiAbs).href,
|
|
81
|
+
absPath: tuiAbs,
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 从 bin 文件路径反推 install path (package 根目录)。
|
|
87
|
+
*
|
|
88
|
+
* 用于 CLI: bin 位于 <pkg>/bin/<name>.js,install path = <pkg>/。
|
|
89
|
+
* 自动 realpath 解析 symlink (npm global install 会建 symlink)。
|
|
90
|
+
*/
|
|
91
|
+
export function resolveInstallPathFromBin(binFilePath) {
|
|
92
|
+
if (!isAbsolute(binFilePath)) {
|
|
93
|
+
throw new Error(`bin path must be absolute: ${binFilePath}`);
|
|
94
|
+
}
|
|
95
|
+
const realBin = realpathSync(binFilePath);
|
|
96
|
+
// <pkg>/bin/<name>.js → dirname = <pkg>/bin → dirname = <pkg>
|
|
97
|
+
return dirname(dirname(realBin));
|
|
98
|
+
}
|
|
99
|
+
// ── JSON I/O ──
|
|
100
|
+
/**
|
|
101
|
+
* 读 JSON config。文件不存在或为空 → 返回 {}。
|
|
102
|
+
*
|
|
103
|
+
* 严格模式 (v1.18 收口 — 旧 tui-install.ts 行为):
|
|
104
|
+
* - 文件不存在或空 → 返回 {} (允许新装)
|
|
105
|
+
* - JSON.parse 失败 → 抛 Error (writePluginEntry 捕获并返回 error, 不覆盖用户数据)
|
|
106
|
+
* - 根节点非 object (array / null / 标量) → 抛 Error
|
|
107
|
+
*
|
|
108
|
+
* 不抛的"宽松"行为会静默覆盖用户已损坏的 config, 风险高。
|
|
109
|
+
* 严格模式让 writePluginEntry 显式告诉用户 "config 损坏, 请人工修复"。
|
|
110
|
+
*/
|
|
111
|
+
export function readJsonConfig(path) {
|
|
112
|
+
if (!existsSync(path)) {
|
|
113
|
+
return {};
|
|
114
|
+
}
|
|
115
|
+
const text = readFileSync(path, 'utf8');
|
|
116
|
+
if (text.trim().length === 0) {
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
let parsed;
|
|
120
|
+
try {
|
|
121
|
+
parsed = JSON.parse(text);
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
125
|
+
throw new Error(`parse error: ${reason}`);
|
|
126
|
+
}
|
|
127
|
+
if (parsed === null) {
|
|
128
|
+
throw new Error(`config root is null, expected object`);
|
|
129
|
+
}
|
|
130
|
+
if (typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
131
|
+
throw new Error(`config root is not an object (got ${Array.isArray(parsed) ? 'array' : typeof parsed})`);
|
|
132
|
+
}
|
|
133
|
+
return parsed;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* 原子写 JSON config。
|
|
137
|
+
*
|
|
138
|
+
* 流程: mkdir -p parent → 写 path.tmp → rename 到 path。
|
|
139
|
+
* rename 在同一文件系统下是原子操作,避免半写状态。
|
|
140
|
+
*/
|
|
141
|
+
export function writeJsonConfig(path, data) {
|
|
142
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
143
|
+
const tmpPath = `${path}.tmp`;
|
|
144
|
+
writeFileSync(tmpPath, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
145
|
+
renameSync(tmpPath, path);
|
|
146
|
+
}
|
|
147
|
+
// ── 幂等检查 ──
|
|
148
|
+
/** 提取 plugin 数组里所有 entry 的主 spec (string 或 [string, opts] tuple) */
|
|
149
|
+
function collectExistingSpecs(plugin) {
|
|
150
|
+
const specs = new Set();
|
|
151
|
+
if (!Array.isArray(plugin))
|
|
152
|
+
return specs;
|
|
153
|
+
for (const item of plugin) {
|
|
154
|
+
if (typeof item === 'string') {
|
|
155
|
+
specs.add(item);
|
|
156
|
+
}
|
|
157
|
+
else if (Array.isArray(item) && typeof item[0] === 'string') {
|
|
158
|
+
specs.add(item[0]);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return specs;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* 判定 entries 是否已安装。
|
|
165
|
+
*
|
|
166
|
+
* 只检查 plugin 数组:已有相同 file:// URL 的视为已安装 (string / tuple 形式)
|
|
167
|
+
* 不写 _plugin_origins — opencode 严格校验 config JSON schema,不认未知 key。
|
|
168
|
+
*
|
|
169
|
+
* 返回 true = 全部 entries 都不必再写 (no-op)
|
|
170
|
+
* 返回 false = 至少一个 entry 还没装
|
|
171
|
+
*/
|
|
172
|
+
export function isAlreadyInstalled(entries, existing) {
|
|
173
|
+
if (entries.length === 0)
|
|
174
|
+
return true;
|
|
175
|
+
const existingSpecs = collectExistingSpecs(existing['plugin']);
|
|
176
|
+
for (const entry of entries) {
|
|
177
|
+
if (existingSpecs.has(entry.path)) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
// ── 主入口 ──
|
|
184
|
+
/**
|
|
185
|
+
* 把 plugin entries 写入 config file 的 `plugin` 数组 (idempotent)。
|
|
186
|
+
*
|
|
187
|
+
* 行为契约:
|
|
188
|
+
* - 文件不存在 → 创建 (含 $schema + plugin 数组)
|
|
189
|
+
* - 已有 plugin 数组 → 保留,append 不重复 entry
|
|
190
|
+
* - 已有非 plugin 字段 → 完全保留
|
|
191
|
+
* - 写失败 → 返回 { changed: false, error },不抛
|
|
192
|
+
*
|
|
193
|
+
* 注意: 不写 _plugin_origins 等非标准 key — opencode 严格校验 config schema。
|
|
194
|
+
*
|
|
195
|
+
* @param configPath 目标 config 文件绝对路径
|
|
196
|
+
* @param entries 要注册的 plugin entries (server/tui 之一或两者)
|
|
197
|
+
*/
|
|
198
|
+
export function writePluginEntry(configPath, entries) {
|
|
199
|
+
if (entries.length === 0) {
|
|
200
|
+
return { changed: false, configPath, skippedPaths: [] };
|
|
201
|
+
}
|
|
202
|
+
let config;
|
|
203
|
+
try {
|
|
204
|
+
config = readJsonConfig(configPath);
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
208
|
+
return { changed: false, configPath, error: `read failed: ${reason}` };
|
|
209
|
+
}
|
|
210
|
+
// 分离: 真正要新增的 vs 已存在的(只检查 plugin 数组,不写 _plugin_origins)
|
|
211
|
+
const existingSpecs = collectExistingSpecs(config['plugin']);
|
|
212
|
+
const currentPlugin = Array.isArray(config['plugin']) ? config['plugin'] : [];
|
|
213
|
+
const toAdd = [];
|
|
214
|
+
const skipped = [];
|
|
215
|
+
for (const entry of entries) {
|
|
216
|
+
if (existingSpecs.has(entry.path)) {
|
|
217
|
+
skipped.push(entry.path);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
toAdd.push(entry.path);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (toAdd.length === 0) {
|
|
224
|
+
return {
|
|
225
|
+
changed: false,
|
|
226
|
+
configPath,
|
|
227
|
+
skippedPaths: skipped,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
// 追加 spec
|
|
231
|
+
for (const spec of toAdd) {
|
|
232
|
+
currentPlugin.push(spec);
|
|
233
|
+
}
|
|
234
|
+
config['plugin'] = currentPlugin;
|
|
235
|
+
// 新文件加 $schema (与 tui-install.ts 一致)
|
|
236
|
+
if (!('$schema' in config)) {
|
|
237
|
+
config['$schema'] = configPath.endsWith('tui.json')
|
|
238
|
+
? TUI_SCHEMA_URL
|
|
239
|
+
: OPENCODE_SCHEMA_URL;
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
writeJsonConfig(configPath, config);
|
|
243
|
+
}
|
|
244
|
+
catch (err) {
|
|
245
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
246
|
+
return { changed: false, configPath, error: `write failed: ${reason}` };
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
changed: true,
|
|
250
|
+
configPath,
|
|
251
|
+
addedPaths: toAdd,
|
|
252
|
+
skippedPaths: skipped,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MSM flag 规范解析 + path-arg 校验(v1.2 修复)
|
|
3
|
+
*
|
|
4
|
+
* 设计目标:让 plugin 兼容 **两种 msm schema**:
|
|
5
|
+
*
|
|
6
|
+
* **v1 schema**(主仓实际使用):
|
|
7
|
+
* ```json
|
|
8
|
+
* flags: [{ "flag": "--check <路径>", "description": "..." }]
|
|
9
|
+
* ```
|
|
10
|
+
* - 字段是 `flag`(含 `--` 整字符串)
|
|
11
|
+
* - 路径型用 valueHint `<路径>` / `<path>` 标记
|
|
12
|
+
* - 无 type 字段
|
|
13
|
+
*
|
|
14
|
+
* **v0 schema**(plugin 仓设计):
|
|
15
|
+
* ```json
|
|
16
|
+
* flags: [{ "name": "check", "type": "path", "description": "..." }]
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* 输出统一 NormalizedFlag[]:
|
|
20
|
+
* - name: 'check'(不带 `--`)
|
|
21
|
+
* - hasValue: boolean
|
|
22
|
+
* - valueHint: '路径'(v1 schema 提取)
|
|
23
|
+
* - isPath: boolean
|
|
24
|
+
*
|
|
25
|
+
* 校验流程:
|
|
26
|
+
* 1. CLI args 字符串 → tokenize(支持引号)
|
|
27
|
+
* 2. token pair `--flag value` → 查 normalizeFlags 找 isPath 标记
|
|
28
|
+
* 3. 路径型 → isPathInside 黑名单 + symlink 防御
|
|
29
|
+
*/
|
|
30
|
+
/** 原始 flag 字段(v1 schema 用 `flag`,v0 schema 用 `name`) */
|
|
31
|
+
type RawFlag = {
|
|
32
|
+
flag?: string;
|
|
33
|
+
name?: string;
|
|
34
|
+
type?: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
required?: boolean;
|
|
37
|
+
default?: unknown;
|
|
38
|
+
};
|
|
39
|
+
/** 规范化后的 flag(plugin 内部统一使用) */
|
|
40
|
+
export type NormalizedFlag = {
|
|
41
|
+
name: string;
|
|
42
|
+
hasValue: boolean;
|
|
43
|
+
valueHint: string | undefined;
|
|
44
|
+
isPath: boolean;
|
|
45
|
+
type: string | undefined;
|
|
46
|
+
required: boolean;
|
|
47
|
+
};
|
|
48
|
+
/** 规范化单个 flag(兼容 v0 / v1 schema) */
|
|
49
|
+
export declare function normalizeFlag(raw: RawFlag): NormalizedFlag | null;
|
|
50
|
+
/** 批量规范化 */
|
|
51
|
+
export declare function normalizeFlags(rawFlags: RawFlag[]): NormalizedFlag[];
|
|
52
|
+
/** 从 normalized flags 提取所有 path-arg 名字 */
|
|
53
|
+
export declare function getPathArgNames(normalized: NormalizedFlag[]): string[];
|
|
54
|
+
/**
|
|
55
|
+
* 轻量 shell tokenize(支持单/双引号 + 转义)。
|
|
56
|
+
* 不支持 glob / 变量展开(msm 脚本不需要)。
|
|
57
|
+
* 示例:
|
|
58
|
+
* '--check .opencode/x' → ['--check', '.opencode/x']
|
|
59
|
+
* '--name "hello world"' → ['--name', 'hello world']
|
|
60
|
+
* '--root' → ['--root']
|
|
61
|
+
*/
|
|
62
|
+
export declare function tokenizeArgs(args: string): string[];
|
|
63
|
+
/**
|
|
64
|
+
* 校验 tokenized CLI args 中的 path-arg。
|
|
65
|
+
* 启发式:解析 `--flag value` / `--flag=value` 形式,对 valueHint 标为 path 的 flag 做 isPathInside 检查。
|
|
66
|
+
*
|
|
67
|
+
* 行为细节:
|
|
68
|
+
* - 非 path-arg 不校验
|
|
69
|
+
* - token 顺序解析,--flag 后面跟的 token 是 value
|
|
70
|
+
* - = 形式 --flag=value 不校验(罕见)
|
|
71
|
+
* - 文件不存在(写文件场景)→ 通过
|
|
72
|
+
* - symlink 防御
|
|
73
|
+
*/
|
|
74
|
+
export declare function validatePathArgsFromTokens(tokens: string[], normalized: NormalizedFlag[], cwdRoot: string): void;
|
|
75
|
+
export {};
|
|
76
|
+
//# sourceMappingURL=msm-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msm-schema.d.ts","sourceRoot":"","sources":["../src/msm-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAOH,wDAAwD;AACxD,KAAK,OAAO,GAAG;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,gCAAgC;AAChC,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAwCF,oCAAoC;AACpC,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAwBjE;AAED,YAAY;AACZ,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,CAOpE;AAED,0CAA0C;AAC1C,wBAAgB,eAAe,CAAC,UAAU,EAAE,cAAc,EAAE,GAAG,MAAM,EAAE,CAEtE;AAID;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAqCnD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EAAE,EAChB,UAAU,EAAE,cAAc,EAAE,EAC5B,OAAO,EAAE,MAAM,GACd,IAAI,CAoCN"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MSM flag 规范解析 + path-arg 校验(v1.2 修复)
|
|
3
|
+
*
|
|
4
|
+
* 设计目标:让 plugin 兼容 **两种 msm schema**:
|
|
5
|
+
*
|
|
6
|
+
* **v1 schema**(主仓实际使用):
|
|
7
|
+
* ```json
|
|
8
|
+
* flags: [{ "flag": "--check <路径>", "description": "..." }]
|
|
9
|
+
* ```
|
|
10
|
+
* - 字段是 `flag`(含 `--` 整字符串)
|
|
11
|
+
* - 路径型用 valueHint `<路径>` / `<path>` 标记
|
|
12
|
+
* - 无 type 字段
|
|
13
|
+
*
|
|
14
|
+
* **v0 schema**(plugin 仓设计):
|
|
15
|
+
* ```json
|
|
16
|
+
* flags: [{ "name": "check", "type": "path", "description": "..." }]
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* 输出统一 NormalizedFlag[]:
|
|
20
|
+
* - name: 'check'(不带 `--`)
|
|
21
|
+
* - hasValue: boolean
|
|
22
|
+
* - valueHint: '路径'(v1 schema 提取)
|
|
23
|
+
* - isPath: boolean
|
|
24
|
+
*
|
|
25
|
+
* 校验流程:
|
|
26
|
+
* 1. CLI args 字符串 → tokenize(支持引号)
|
|
27
|
+
* 2. token pair `--flag value` → 查 normalizeFlags 找 isPath 标记
|
|
28
|
+
* 3. 路径型 → isPathInside 黑名单 + symlink 防御
|
|
29
|
+
*/
|
|
30
|
+
import { resolve as resolvePath } from 'node:path';
|
|
31
|
+
import { realpathSync, existsSync } from 'node:fs';
|
|
32
|
+
import { isPathInside } from './util/git.js';
|
|
33
|
+
import { MsmPathEscapeError, MsmSymlinkError } from './errors.js';
|
|
34
|
+
/** v1 schema 的 valueHint 视为"路径型" */
|
|
35
|
+
const PATH_VALUE_HINTS = new Set([
|
|
36
|
+
'路径',
|
|
37
|
+
'path',
|
|
38
|
+
'file',
|
|
39
|
+
'filePath',
|
|
40
|
+
'filepath',
|
|
41
|
+
'dir',
|
|
42
|
+
'directory',
|
|
43
|
+
'文件',
|
|
44
|
+
'目录',
|
|
45
|
+
'url',
|
|
46
|
+
'uri',
|
|
47
|
+
]);
|
|
48
|
+
/** v0 schema 的 type 视为"路径型" */
|
|
49
|
+
const PATH_ARG_TYPES = new Set([
|
|
50
|
+
'path',
|
|
51
|
+
'file',
|
|
52
|
+
'filePath',
|
|
53
|
+
'filepath',
|
|
54
|
+
'dir',
|
|
55
|
+
'directory',
|
|
56
|
+
]);
|
|
57
|
+
/** v1 schema flag 字符串 → {name, hasValue, valueHint} */
|
|
58
|
+
function parseV1Flag(flagStr) {
|
|
59
|
+
const m = /^--([\w-]+)(?:[=\s]+(?:<(.+?)>))?$/.exec(flagStr.trim());
|
|
60
|
+
if (!m) {
|
|
61
|
+
return { name: flagStr, hasValue: false, valueHint: undefined };
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
name: m[1],
|
|
65
|
+
hasValue: m[2] !== undefined,
|
|
66
|
+
valueHint: m[2],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/** 规范化单个 flag(兼容 v0 / v1 schema) */
|
|
70
|
+
export function normalizeFlag(raw) {
|
|
71
|
+
if (typeof raw.name === 'string') {
|
|
72
|
+
const type = raw.type;
|
|
73
|
+
return {
|
|
74
|
+
name: raw.name,
|
|
75
|
+
hasValue: type !== 'boolean',
|
|
76
|
+
valueHint: undefined,
|
|
77
|
+
isPath: type !== undefined && PATH_ARG_TYPES.has(type),
|
|
78
|
+
type,
|
|
79
|
+
required: raw.required ?? false,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (typeof raw.flag === 'string') {
|
|
83
|
+
const parsed = parseV1Flag(raw.flag);
|
|
84
|
+
return {
|
|
85
|
+
name: parsed.name,
|
|
86
|
+
hasValue: parsed.hasValue,
|
|
87
|
+
valueHint: parsed.valueHint,
|
|
88
|
+
isPath: parsed.valueHint !== undefined && PATH_VALUE_HINTS.has(parsed.valueHint),
|
|
89
|
+
type: undefined,
|
|
90
|
+
required: false,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
/** 批量规范化 */
|
|
96
|
+
export function normalizeFlags(rawFlags) {
|
|
97
|
+
const out = [];
|
|
98
|
+
for (const raw of rawFlags) {
|
|
99
|
+
const norm = normalizeFlag(raw);
|
|
100
|
+
if (norm !== null)
|
|
101
|
+
out.push(norm);
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
/** 从 normalized flags 提取所有 path-arg 名字 */
|
|
106
|
+
export function getPathArgNames(normalized) {
|
|
107
|
+
return normalized.filter((f) => f.isPath).map((f) => f.name);
|
|
108
|
+
}
|
|
109
|
+
/* ===== CLI tokenize + path-arg 校验(v1.2 简化为启发式) ===== */
|
|
110
|
+
/**
|
|
111
|
+
* 轻量 shell tokenize(支持单/双引号 + 转义)。
|
|
112
|
+
* 不支持 glob / 变量展开(msm 脚本不需要)。
|
|
113
|
+
* 示例:
|
|
114
|
+
* '--check .opencode/x' → ['--check', '.opencode/x']
|
|
115
|
+
* '--name "hello world"' → ['--name', 'hello world']
|
|
116
|
+
* '--root' → ['--root']
|
|
117
|
+
*/
|
|
118
|
+
export function tokenizeArgs(args) {
|
|
119
|
+
const out = [];
|
|
120
|
+
let cur = '';
|
|
121
|
+
let inSingle = false;
|
|
122
|
+
let inDouble = false;
|
|
123
|
+
let hasToken = false;
|
|
124
|
+
for (let i = 0; i < args.length; i++) {
|
|
125
|
+
const c = args[i];
|
|
126
|
+
if (c === '\\' && i + 1 < args.length) {
|
|
127
|
+
cur += args[i + 1];
|
|
128
|
+
i++;
|
|
129
|
+
hasToken = true;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (c === "'" && !inDouble) {
|
|
133
|
+
inSingle = !inSingle;
|
|
134
|
+
hasToken = true;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (c === '"' && !inSingle) {
|
|
138
|
+
inDouble = !inDouble;
|
|
139
|
+
hasToken = true;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (/\s/.test(c) && !inSingle && !inDouble) {
|
|
143
|
+
if (hasToken) {
|
|
144
|
+
out.push(cur);
|
|
145
|
+
cur = '';
|
|
146
|
+
hasToken = false;
|
|
147
|
+
}
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
cur += c;
|
|
151
|
+
hasToken = true;
|
|
152
|
+
}
|
|
153
|
+
if (hasToken)
|
|
154
|
+
out.push(cur);
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* 校验 tokenized CLI args 中的 path-arg。
|
|
159
|
+
* 启发式:解析 `--flag value` / `--flag=value` 形式,对 valueHint 标为 path 的 flag 做 isPathInside 检查。
|
|
160
|
+
*
|
|
161
|
+
* 行为细节:
|
|
162
|
+
* - 非 path-arg 不校验
|
|
163
|
+
* - token 顺序解析,--flag 后面跟的 token 是 value
|
|
164
|
+
* - = 形式 --flag=value 不校验(罕见)
|
|
165
|
+
* - 文件不存在(写文件场景)→ 通过
|
|
166
|
+
* - symlink 防御
|
|
167
|
+
*/
|
|
168
|
+
export function validatePathArgsFromTokens(tokens, normalized, cwdRoot) {
|
|
169
|
+
// path-arg names 集合
|
|
170
|
+
const pathArgNames = new Set(getPathArgNames(normalized));
|
|
171
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
172
|
+
const tok = tokens[i];
|
|
173
|
+
// 匹配 --flag
|
|
174
|
+
const m = /^--([\w-]+)$/.exec(tok);
|
|
175
|
+
if (!m)
|
|
176
|
+
continue;
|
|
177
|
+
const flagName = m[1];
|
|
178
|
+
if (!pathArgNames.has(flagName))
|
|
179
|
+
continue;
|
|
180
|
+
// 取下一个 token 作为 value
|
|
181
|
+
const value = tokens[i + 1];
|
|
182
|
+
if (value === undefined || value.startsWith('--'))
|
|
183
|
+
continue;
|
|
184
|
+
if (typeof value !== 'string' || value.trim() === '')
|
|
185
|
+
continue;
|
|
186
|
+
const abs = resolvePath(cwdRoot, value);
|
|
187
|
+
if (!isPathInside(cwdRoot, abs)) {
|
|
188
|
+
throw new MsmPathEscapeError('msm_exec', flagName, value, abs);
|
|
189
|
+
}
|
|
190
|
+
if (existsSync(abs)) {
|
|
191
|
+
let real;
|
|
192
|
+
try {
|
|
193
|
+
real = realpathSync(abs);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
throw new MsmSymlinkError('msm_exec', flagName, value, abs, 'realpath resolution failed');
|
|
197
|
+
}
|
|
198
|
+
if (real !== abs) {
|
|
199
|
+
throw new MsmSymlinkError('msm_exec', flagName, value, abs, `symlink detected: ${abs} → ${real}`);
|
|
200
|
+
}
|
|
201
|
+
if (!isPathInside(cwdRoot, real)) {
|
|
202
|
+
throw new MsmSymlinkError('msm_exec', flagName, value, abs, `symlink points outside cwdRoot: ${real}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=msm-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msm-schema.js","sourceRoot":"","sources":["../src/msm-schema.ts"],"names":[],"mappings":"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"}
|
package/dist/msm.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* msm.ts
|
|
3
|
+
*
|
|
4
|
+
* 工具集(3 个):
|
|
5
|
+
* - msm_list : PRIMARY — 列出所有 MSM
|
|
6
|
+
* - msm_exec : PRIMARY — 执行 MSM / 协议元命令
|
|
7
|
+
* - msm_admin : 注册 / 注销 MSM(合并 register/deregister)
|
|
8
|
+
*
|
|
9
|
+
* 注意:bash override (RR3) 已于 2026-06-08 移除。
|
|
10
|
+
*/
|
|
11
|
+
import { type ToolDefinition } from '@opencode-ai/plugin';
|
|
12
|
+
import { type MechEntry, type RegistryFile } from './config-schema.js';
|
|
13
|
+
/** 加载 mech-registry.json(v0 简化:实例内一份) */
|
|
14
|
+
/** 支持两种 schema:
|
|
15
|
+
* - v1 包装格式:{ version, description, entries: [...] }
|
|
16
|
+
* - 数组格式:[...]
|
|
17
|
+
* 返回统一 MechEntry[]
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadMechRegistryFrom(cwdRoot: string, instanceName: string): MechEntry[];
|
|
20
|
+
export declare function loadRegistryFile(cwdRoot: string, instanceName: string): RegistryFile;
|
|
21
|
+
export declare function writeRegistryFile(cwdRoot: string, instanceName: string, file: RegistryFile): void;
|
|
22
|
+
export declare const msmListTool: ToolDefinition;
|
|
23
|
+
export declare const msmExecTool: ToolDefinition;
|
|
24
|
+
export declare const msmAdminTool: ToolDefinition;
|
|
25
|
+
//# sourceMappingURL=msm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msm.d.ts","sourceRoot":"","sources":["../src/msm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAehE,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAC;AAM5B,yCAAyC;AACzC;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,EAAE,CAEvF;AAMD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,YAAY,CAqCpF;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAUjG;AAkBD,eAAO,MAAM,WAAW,EAAE,cAyBxB,CAAC;AAUH,eAAO,MAAM,WAAW,EAAE,cAmDxB,CAAC;AA4GH,eAAO,MAAM,YAAY,EAAE,cA+DzB,CAAC"}
|