@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
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Permission Guards Hook 工厂
|
|
3
|
+
*
|
|
4
|
+
* 包含:tool.execute.before hook
|
|
5
|
+
* 职责(RR5 + v1.6 补全):
|
|
6
|
+
* 1. read / edit / write 工具的 path 字段强制在 cwdRoot 内(RR5 hard block;v1.6 加 edit/write)
|
|
7
|
+
* 2. symlink 防御(v1.6 扩展 v1-1 msm-schema 的 realpath 逻辑到 tool.execute.before)
|
|
8
|
+
*
|
|
9
|
+
* 注意:bash 禁令于 2026-06-08 移除(旧 RR3),改用运行时开关:
|
|
10
|
+
* TUI slash command /serenity-bash-on|off|status 控制,通过文件 IPC 通信,
|
|
11
|
+
* 在 tool.execute.before 中做静默拒绝。默认 bash 启用。
|
|
12
|
+
*
|
|
13
|
+
* 设计:v1.6 RR5 补全 = plugin **接管**权限管理(不再依赖 opencode.json 静态 allow)
|
|
14
|
+
* - 主仓 opencode.json 已复原 read/edit = "ask"(commit fe19b5e)
|
|
15
|
+
* - LLM 在 cwdRoot 内时:v1.3-v2 auto-reply 仍处理 "ask" 弹窗
|
|
16
|
+
* - LLM 在 cwdRoot 外时:plugin hard block 早于 opencode 弹窗生效
|
|
17
|
+
*
|
|
18
|
+
* L3 验证:单 hook 抛错会中断整条 Effect 链(plugin/index.ts:286-299),
|
|
19
|
+
* 所以抛错已被 safeHook 包装为 silent(util.ts)
|
|
20
|
+
*/
|
|
21
|
+
import { resolve as pathResolve } from 'node:path';
|
|
22
|
+
import { realpathSync, existsSync } from 'node:fs';
|
|
23
|
+
import { isPathInside } from '../util/git.js';
|
|
24
|
+
import { getState, ensureReady } from '../state.js';
|
|
25
|
+
import { isHookEnabled } from './util.js';
|
|
26
|
+
import { log } from '../util/log.js';
|
|
27
|
+
import { isBashDisabled } from '../bash-toggle.js';
|
|
28
|
+
/**
|
|
29
|
+
* v1.6 字段名提取:
|
|
30
|
+
* - 显式列表:command, path, filePath, file, cwd(v0.1-3 已有)
|
|
31
|
+
* - 启发式后缀:*path* / *file* / *dir* / *Path* / *File* / *Dir*(v1.6 加)
|
|
32
|
+
* 覆盖:targetPath / sourceFilePath / outputDir / newPath / etc.
|
|
33
|
+
*/
|
|
34
|
+
export function extractPathsFromArgs(args) {
|
|
35
|
+
const candidates = [];
|
|
36
|
+
// 显式字段(不区分大小写)
|
|
37
|
+
const explicitKeys = new Set(['command', 'path', 'filepath', 'file', 'cwd']);
|
|
38
|
+
for (const [key, value] of Object.entries(args)) {
|
|
39
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
40
|
+
continue;
|
|
41
|
+
if (explicitKeys.has(key.toLowerCase())) {
|
|
42
|
+
candidates.push(value);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// 启发式后缀(覆盖大多数 LLM 工具的命名习惯)
|
|
46
|
+
for (const [key, value] of Object.entries(args)) {
|
|
47
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
48
|
+
continue;
|
|
49
|
+
if (explicitKeys.has(key.toLowerCase()))
|
|
50
|
+
continue; // 跳过已加的
|
|
51
|
+
const lower = key.toLowerCase();
|
|
52
|
+
if (lower.includes('path') || lower.includes('file') || lower.includes('dir')) {
|
|
53
|
+
candidates.push(value);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return candidates;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 路径越界判定 + symlink 防御
|
|
60
|
+
* 返回:
|
|
61
|
+
* - 'inside':路径在 cwdRoot 内
|
|
62
|
+
* - 'outside':路径解析后在 cwdRoot 外
|
|
63
|
+
* - 'symlink':是 symlink 且 realpath 在 cwdRoot 外
|
|
64
|
+
* - 'unparseable':路径无法解析
|
|
65
|
+
* - 'not-exist':文件不存在(写场景,无法 realpath)
|
|
66
|
+
*/
|
|
67
|
+
function classifyPath(value, cwdRoot) {
|
|
68
|
+
if (value.startsWith('/') || /^[a-zA-Z]:[\\\/]/.test(value)) {
|
|
69
|
+
let abs;
|
|
70
|
+
try {
|
|
71
|
+
abs = pathResolve(value);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return 'unparseable';
|
|
75
|
+
}
|
|
76
|
+
if (!isPathInside(cwdRoot, abs))
|
|
77
|
+
return 'outside';
|
|
78
|
+
if (existsSync(abs)) {
|
|
79
|
+
try {
|
|
80
|
+
const real = realpathSync(abs);
|
|
81
|
+
if (real !== abs)
|
|
82
|
+
return 'symlink';
|
|
83
|
+
if (!isPathInside(cwdRoot, real))
|
|
84
|
+
return 'symlink';
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return 'unparseable';
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
return 'not-exist'; // 写文件场景合理
|
|
92
|
+
}
|
|
93
|
+
return 'inside';
|
|
94
|
+
}
|
|
95
|
+
return 'inside'; // 相对路径由 msm 子进程处理;tool 调用前已是绝对路径
|
|
96
|
+
}
|
|
97
|
+
const toolExecuteBeforeImpl = async (input, _output) => {
|
|
98
|
+
// v0.1: 阻塞等待 Phase 2 完成(失败时:放行所有 = plugin 不工作)
|
|
99
|
+
try {
|
|
100
|
+
await ensureReady();
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const state = getState();
|
|
106
|
+
// SDK 1.15.13: tool.execute.before signature = (input, output) where output.args is the tool call's arguments
|
|
107
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
108
|
+
const args = _output?.args ?? {};
|
|
109
|
+
const paths = extractPathsFromArgs(args);
|
|
110
|
+
// v1.6: edit + write 一起 hard block(read 已有;webfetch A2 决定不动)
|
|
111
|
+
// v1.6: grep 加入 — path 参数强制在 cwdRoot 内,禁止搜索宁静号之外的内容
|
|
112
|
+
// v0.1: glob 加入 — 与 grep 同级限制
|
|
113
|
+
if (input.tool === 'read' || input.tool === 'edit' || input.tool === 'write' || input.tool === 'grep' || input.tool === 'glob') {
|
|
114
|
+
for (const p of paths) {
|
|
115
|
+
const verdict = classifyPath(p, state.cwdRoot);
|
|
116
|
+
if (verdict === 'outside' || verdict === 'symlink') {
|
|
117
|
+
log.warn('guard', `${input.tool} path ${verdict} cwdRoot`, { path: p, cwdRoot: state.cwdRoot });
|
|
118
|
+
throw new Error(`[serenity] ${input.tool} path "${p}" is ${verdict} the serenity workspace root "${state.cwdRoot}" (RR5).`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// webfetch A2 决定不动(保持 v0.1-3 行为)
|
|
123
|
+
if (input.tool === 'webfetch') {
|
|
124
|
+
for (const p of paths) {
|
|
125
|
+
const verdict = classifyPath(p, state.cwdRoot);
|
|
126
|
+
if (verdict === 'outside' || verdict === 'symlink') {
|
|
127
|
+
log.warn('guard', `webfetch path ${verdict} cwdRoot`, { path: p, cwdRoot: state.cwdRoot });
|
|
128
|
+
throw new Error(`[serenity] webfetch path "${p}" is ${verdict} the serenity workspace root "${state.cwdRoot}" (RR5).`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// bash toggle: 静默拒绝(文件 IPC,TUI slash command 控制)
|
|
133
|
+
if (input.tool === 'bash' && isBashDisabled()) {
|
|
134
|
+
throw new Error(`bash is disabled by user, use msm instead`);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
/** 工厂:返回 permission guards 相关的 hooks 集合
|
|
138
|
+
*
|
|
139
|
+
* v1.6 关键修正:permission-guards **不**走 safeHook。
|
|
140
|
+
* 原因:safeHook 的"silent 策略"会吞掉 throw,导致 RR5 hard block 失效。
|
|
141
|
+
* L3 验证:单 hook 抛错中断整条 Effect 链(plugin/index.ts:286-299)——
|
|
142
|
+
* 这正是 RR5 hard block 想要的行为,所以应当让它**传播**给 opencode。
|
|
143
|
+
* 其他 hook(compacting / shell-env)保留 safeHook(throw 不应硬中断)。
|
|
144
|
+
*/
|
|
145
|
+
export function createPermissionGuards(config) {
|
|
146
|
+
const hooks = {};
|
|
147
|
+
if (isHookEnabled('tool.execute.before', config)) {
|
|
148
|
+
// 不走 safeHook —— 让 RR5 throw 真正生效
|
|
149
|
+
hooks['tool.execute.before'] = toolExecuteBeforeImpl;
|
|
150
|
+
}
|
|
151
|
+
return hooks;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=permission-guards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-guards.js","sourceRoot":"","sources":["../../src/hooks/permission-guards.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell Environment Hook 工厂
|
|
3
|
+
*
|
|
4
|
+
* 包含:shell.env hook
|
|
5
|
+
* 职责(v0.1 增强):
|
|
6
|
+
* - 注入 HOME_SERENITY_ROOT = state.cwdRoot(所有 bash 子进程可见)
|
|
7
|
+
* - 注入 SERENITY_INSTANCE = state.instanceName
|
|
8
|
+
* - 注入 SERENITY_PLUGIN_VERSION(plugin 自报版本,便于 msm 脚本判定)
|
|
9
|
+
*
|
|
10
|
+
* 注意:v0 禁 bash(同名 tool 覆盖 + permission.bash=deny),
|
|
11
|
+
* shell.env 仍生效到 msm_exec 子进程 + 用户终端(如 msm 间接调 bash)
|
|
12
|
+
*/
|
|
13
|
+
import type { Hooks } from '@opencode-ai/plugin';
|
|
14
|
+
import { type HookConfig } from './util.js';
|
|
15
|
+
/** 工厂:返回 shell env 相关的 hooks 集合
|
|
16
|
+
*
|
|
17
|
+
* v1.12: 改用 safeCreateHook(factory pattern)
|
|
18
|
+
*/
|
|
19
|
+
export declare function createShellEnv(config?: HookConfig): Partial<Hooks>;
|
|
20
|
+
//# sourceMappingURL=shell-env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-env.d.ts","sourceRoot":"","sources":["../../src/hooks/shell-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAiB5D;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAQlE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell Environment Hook 工厂
|
|
3
|
+
*
|
|
4
|
+
* 包含:shell.env hook
|
|
5
|
+
* 职责(v0.1 增强):
|
|
6
|
+
* - 注入 HOME_SERENITY_ROOT = state.cwdRoot(所有 bash 子进程可见)
|
|
7
|
+
* - 注入 SERENITY_INSTANCE = state.instanceName
|
|
8
|
+
* - 注入 SERENITY_PLUGIN_VERSION(plugin 自报版本,便于 msm 脚本判定)
|
|
9
|
+
*
|
|
10
|
+
* 注意:v0 禁 bash(同名 tool 覆盖 + permission.bash=deny),
|
|
11
|
+
* shell.env 仍生效到 msm_exec 子进程 + 用户终端(如 msm 间接调 bash)
|
|
12
|
+
*/
|
|
13
|
+
import { getState, ensureReady } from '../state.js';
|
|
14
|
+
import { safeCreateHook } from './util.js';
|
|
15
|
+
import pkg from '../../package.json' with { type: 'json' };
|
|
16
|
+
const shellEnvImpl = async (_input, output) => {
|
|
17
|
+
try {
|
|
18
|
+
await ensureReady();
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const state = getState();
|
|
24
|
+
output.env.HOME_SERENITY_ROOT = state.cwdRoot;
|
|
25
|
+
output.env.SERENITY_INSTANCE = state.instanceName;
|
|
26
|
+
// v1.18 统一:与 tui.ts 同源 (package.json#version),release 时改一处
|
|
27
|
+
output.env.SERENITY_PLUGIN_VERSION = pkg.version;
|
|
28
|
+
};
|
|
29
|
+
/** 工厂:返回 shell env 相关的 hooks 集合
|
|
30
|
+
*
|
|
31
|
+
* v1.12: 改用 safeCreateHook(factory pattern)
|
|
32
|
+
*/
|
|
33
|
+
export function createShellEnv(config) {
|
|
34
|
+
const hooks = {};
|
|
35
|
+
hooks['shell.env'] = safeCreateHook('shell.env', () => shellEnvImpl, config);
|
|
36
|
+
return hooks;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=shell-env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell-env.js","sourceRoot":"","sources":["../../src/hooks/shell-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,cAAc,EAAmB,MAAM,WAAW,CAAC;AAC5D,OAAO,GAAG,MAAM,oBAAoB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAE3D,MAAM,YAAY,GAAoC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;IAC7E,IAAI,CAAC;QACH,MAAM,WAAW,EAAE,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,MAAM,CAAC,GAAG,CAAC,kBAAkB,GAAG,KAAK,CAAC,OAAO,CAAC;IAC9C,MAAM,CAAC,GAAG,CAAC,iBAAiB,GAAG,KAAK,CAAC,YAAY,CAAC;IAClD,2DAA2D;IAC3D,MAAM,CAAC,GAAG,CAAC,uBAAuB,GAAG,GAAG,CAAC,OAAO,CAAC;AACnD,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,MAAmB;IAChD,MAAM,KAAK,GAAmB,EAAE,CAAC;IACjC,KAAK,CAAC,WAAW,CAAC,GAAG,cAAc,CACjC,WAAW,EACX,GAAG,EAAE,CAAC,YAAY,EAClB,MAAM,CACP,CAAC;IACF,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook 工厂公共工具(oMo 模式 v1.12 增强)
|
|
3
|
+
*
|
|
4
|
+
* v1.12 新增 3 项能力(D25 决策 — 仿 omo safeHook 模式 + 失败自动 disable):
|
|
5
|
+
* 1. `isHookEnabled(name, config?)` — 集中开关
|
|
6
|
+
* - 显式 config[name] === false → 禁用
|
|
7
|
+
* - 环境变量 `SERENITY_HOOK_DISABLED_<NAME>=1` → 禁用
|
|
8
|
+
* - module-level disable(plugin process lifetime)→ 禁用
|
|
9
|
+
* 2. `disableHook(name, reason?)` / `isHookDisabled(name)` — 手动 / 自动禁用
|
|
10
|
+
* 3. `safeCreateHook(name, factory, config?)` — 工厂包装(v1.12 推荐 API)
|
|
11
|
+
* - isHookEnabled === false → 返回 no-op 函数(hook 签名匹配)
|
|
12
|
+
* - factory 抛错 → 标记 disabled + 返回 no-op(**避免 retry storm**)
|
|
13
|
+
* - 注册后 hook 抛错 → 标记 disabled + 不 rethrow(**不传播 = 整 plugin 不挂**)
|
|
14
|
+
* - 错误信息用 log.debug 记录 + module-level _sessionErrors Map 保留
|
|
15
|
+
*
|
|
16
|
+
* state lifetime: plugin process lifetime(不是 chat session)
|
|
17
|
+
* - 一个 opencode 进程 = 一个 plugin 进程 = 一组 disable 状态
|
|
18
|
+
* - 测试用 `_resetSessionHookGuard()` 显式清空(vitest beforeEach 已挂上)
|
|
19
|
+
*
|
|
20
|
+
* 向后兼容(v0.0.1 → v1.12):
|
|
21
|
+
* - 旧 `safeHook(name, impl, config?)` 仍工作;内部已升级走新 isHookEnabled
|
|
22
|
+
* - 旧 `isHookEnabled(name, config?)` 签名不变,行为增强(环境变量 + module-level disable)
|
|
23
|
+
*
|
|
24
|
+
* 与 omo 的差异(v1.12 比 omo 更细):
|
|
25
|
+
* - 粒度更细:每个 hook 独立 disable
|
|
26
|
+
* - 多层 disable:env var / process / config 三道闸
|
|
27
|
+
* - module-level 自动 disable(hook 抛错一次后本次 plugin process 不再试)
|
|
28
|
+
*
|
|
29
|
+
* v1.12 决策:v0.1-3 决策依然有效("plugin 应静默"),所以 hook 抛错**不 rethrow**
|
|
30
|
+
* 但**不静默丢弃** — 错误信息存到 _sessionErrors Map,调试时可查
|
|
31
|
+
*
|
|
32
|
+
* v1.18 收口:HookName / HookConfig / HOOK_NAMES 全部从 config-schema.ts 导入
|
|
33
|
+
* (单一真相源,zod record 严格校验 key ∈ HOOK_NAMES)。
|
|
34
|
+
*/
|
|
35
|
+
import type { Hooks } from '@opencode-ai/plugin';
|
|
36
|
+
import { HOOK_NAMES, type HookName, type HookConfig } from '../config-schema.js';
|
|
37
|
+
export { HOOK_NAMES };
|
|
38
|
+
export type { HookName, HookConfig };
|
|
39
|
+
/** 检查 hook 是否启用(默认 true;任一闸 false 则禁用) */
|
|
40
|
+
export declare function isHookEnabled(name: HookName, config?: HookConfig): boolean;
|
|
41
|
+
/** 显式禁用一个 hook(手动或自动触发) */
|
|
42
|
+
export declare function disableHook(name: HookName, reason?: unknown): void;
|
|
43
|
+
/** 检查 hook 是否已被 session-level 禁用 */
|
|
44
|
+
export declare function isHookDisabled(name: HookName): boolean;
|
|
45
|
+
/** 拿 session-level 错误 Map(调试用) */
|
|
46
|
+
export declare function _getSessionErrors(): ReadonlyMap<HookName, {
|
|
47
|
+
error: unknown;
|
|
48
|
+
ts: Date;
|
|
49
|
+
}>;
|
|
50
|
+
/** 测试用:清空 session-level disable 状态 */
|
|
51
|
+
export declare function _resetSessionHookGuard(): void;
|
|
52
|
+
/**
|
|
53
|
+
* safeCreateHook(v1.12 推荐 API)
|
|
54
|
+
*
|
|
55
|
+
* 包装 hook factory:
|
|
56
|
+
* 1. isHookEnabled === false → 返回 no-op(hook 签名匹配,host 调用但不做事)
|
|
57
|
+
* 2. factory() 抛错 → log.debug + disableHook + 返回 no-op
|
|
58
|
+
* 3. 注册后 hook 抛错 → log.debug + disableHook + 不 rethrow
|
|
59
|
+
*
|
|
60
|
+
* @param name hook 名(用于 env var / session disable 追踪)
|
|
61
|
+
* @param factory 工厂函数(v0 模式:返回 hook impl;这里就是返回 impl 本身)
|
|
62
|
+
* @param config per-hook 配置(可选)
|
|
63
|
+
*/
|
|
64
|
+
export declare function safeCreateHook<K extends HookName>(name: K, factory: () => NonNullable<Hooks[K]>, config?: HookConfig): NonNullable<Hooks[K]>;
|
|
65
|
+
/**
|
|
66
|
+
* safeHook(v0.0.1 旧 API,向后兼容)
|
|
67
|
+
*
|
|
68
|
+
* 行为:
|
|
69
|
+
* - isHookEnabled === false → 返回 undefined(host 不注册 hook,走默认)
|
|
70
|
+
* - 注册后 hook 抛错 → v1.12: disableHook + 不 rethrow
|
|
71
|
+
*
|
|
72
|
+
* 与 safeCreateHook 区别:
|
|
73
|
+
* - safeCreateHook 返回 no-op(hook 已注册但不做事)
|
|
74
|
+
* - safeHook 返回 undefined(hook 未注册)
|
|
75
|
+
*
|
|
76
|
+
* 选择建议:
|
|
77
|
+
* - 旧代码(v0.1-3 起的 hook 工厂)继续用 safeHook,行为兼容
|
|
78
|
+
* - 新代码推荐 safeCreateHook,hook 仍注册(host 期望所有 hook 都存在)
|
|
79
|
+
*/
|
|
80
|
+
export declare function safeHook<K extends HookName>(name: K, impl: NonNullable<Hooks[K]>, config?: HookConfig): NonNullable<Hooks[K]> | undefined;
|
|
81
|
+
//# sourceMappingURL=util.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/hooks/util.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD,OAAO,EACL,UAAU,EACV,KAAK,QAAQ,EACb,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AAsBrC,0CAA0C;AAC1C,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,UAAU,GAAG,OAAO,CAW1E;AAED,2BAA2B;AAC3B,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAMlE;AAED,oCAAoC;AACpC,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAEtD;AAED,kCAAkC;AAClC,wBAAgB,iBAAiB,IAAI,WAAW,CAAC,QAAQ,EAAE;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,EAAE,EAAE,IAAI,CAAA;CAAE,CAAC,CAEvF;AAED,sCAAsC;AACtC,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C;AAgBD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,QAAQ,EAC/C,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,MAAM,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EACpC,MAAM,CAAC,EAAE,UAAU,GAClB,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAyBvB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,QAAQ,EACzC,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAC3B,MAAM,CAAC,EAAE,UAAU,GAClB,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAanC"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook 工厂公共工具(oMo 模式 v1.12 增强)
|
|
3
|
+
*
|
|
4
|
+
* v1.12 新增 3 项能力(D25 决策 — 仿 omo safeHook 模式 + 失败自动 disable):
|
|
5
|
+
* 1. `isHookEnabled(name, config?)` — 集中开关
|
|
6
|
+
* - 显式 config[name] === false → 禁用
|
|
7
|
+
* - 环境变量 `SERENITY_HOOK_DISABLED_<NAME>=1` → 禁用
|
|
8
|
+
* - module-level disable(plugin process lifetime)→ 禁用
|
|
9
|
+
* 2. `disableHook(name, reason?)` / `isHookDisabled(name)` — 手动 / 自动禁用
|
|
10
|
+
* 3. `safeCreateHook(name, factory, config?)` — 工厂包装(v1.12 推荐 API)
|
|
11
|
+
* - isHookEnabled === false → 返回 no-op 函数(hook 签名匹配)
|
|
12
|
+
* - factory 抛错 → 标记 disabled + 返回 no-op(**避免 retry storm**)
|
|
13
|
+
* - 注册后 hook 抛错 → 标记 disabled + 不 rethrow(**不传播 = 整 plugin 不挂**)
|
|
14
|
+
* - 错误信息用 log.debug 记录 + module-level _sessionErrors Map 保留
|
|
15
|
+
*
|
|
16
|
+
* state lifetime: plugin process lifetime(不是 chat session)
|
|
17
|
+
* - 一个 opencode 进程 = 一个 plugin 进程 = 一组 disable 状态
|
|
18
|
+
* - 测试用 `_resetSessionHookGuard()` 显式清空(vitest beforeEach 已挂上)
|
|
19
|
+
*
|
|
20
|
+
* 向后兼容(v0.0.1 → v1.12):
|
|
21
|
+
* - 旧 `safeHook(name, impl, config?)` 仍工作;内部已升级走新 isHookEnabled
|
|
22
|
+
* - 旧 `isHookEnabled(name, config?)` 签名不变,行为增强(环境变量 + module-level disable)
|
|
23
|
+
*
|
|
24
|
+
* 与 omo 的差异(v1.12 比 omo 更细):
|
|
25
|
+
* - 粒度更细:每个 hook 独立 disable
|
|
26
|
+
* - 多层 disable:env var / process / config 三道闸
|
|
27
|
+
* - module-level 自动 disable(hook 抛错一次后本次 plugin process 不再试)
|
|
28
|
+
*
|
|
29
|
+
* v1.12 决策:v0.1-3 决策依然有效("plugin 应静默"),所以 hook 抛错**不 rethrow**
|
|
30
|
+
* 但**不静默丢弃** — 错误信息存到 _sessionErrors Map,调试时可查
|
|
31
|
+
*
|
|
32
|
+
* v1.18 收口:HookName / HookConfig / HOOK_NAMES 全部从 config-schema.ts 导入
|
|
33
|
+
* (单一真相源,zod record 严格校验 key ∈ HOOK_NAMES)。
|
|
34
|
+
*/
|
|
35
|
+
import { log } from '../util/log.js';
|
|
36
|
+
import { HOOK_NAMES, } from '../config-schema.js';
|
|
37
|
+
// 重新导出供现有 import 路径继续工作(hooks/util.js 是公开 API)
|
|
38
|
+
export { HOOK_NAMES };
|
|
39
|
+
/** module-level disable + 错误追踪状态(plugin process lifetime;测试用 reset API) */
|
|
40
|
+
const _sessionDisabled = new Set();
|
|
41
|
+
const _sessionErrors = new Map();
|
|
42
|
+
/**
|
|
43
|
+
* 把 hook name 转成 env var key
|
|
44
|
+
*
|
|
45
|
+
* 例:
|
|
46
|
+
* 'shell.env' → 'SERENITY_HOOK_DISABLED_SHELL_ENV'
|
|
47
|
+
* 'tool.execute.before' → 'SERENITY_HOOK_DISABLED_TOOL_EXECUTE_BEFORE'
|
|
48
|
+
* 'experimental.chat.system.transform' → 'SERENITY_HOOK_DISABLED_EXPERIMENTAL_CHAT_SYSTEM_TRANSFORM'
|
|
49
|
+
*/
|
|
50
|
+
function envVarKeyForHook(name) {
|
|
51
|
+
const normalized = name
|
|
52
|
+
.replace(/\./g, '_')
|
|
53
|
+
.replace(/-/g, '_')
|
|
54
|
+
.toUpperCase();
|
|
55
|
+
return `SERENITY_HOOK_DISABLED_${normalized}`;
|
|
56
|
+
}
|
|
57
|
+
/** 检查 hook 是否启用(默认 true;任一闸 false 则禁用) */
|
|
58
|
+
export function isHookEnabled(name, config) {
|
|
59
|
+
// 1. 环境变量闸
|
|
60
|
+
if (process.env[envVarKeyForHook(name)] === '1')
|
|
61
|
+
return false;
|
|
62
|
+
// 2. session-level disable 闸(v1.12:throw 一次后自动 disable)
|
|
63
|
+
if (_sessionDisabled.has(name))
|
|
64
|
+
return false;
|
|
65
|
+
// 3. config 闸
|
|
66
|
+
if (config && config[name] === false)
|
|
67
|
+
return false;
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
/** 显式禁用一个 hook(手动或自动触发) */
|
|
71
|
+
export function disableHook(name, reason) {
|
|
72
|
+
_sessionDisabled.add(name);
|
|
73
|
+
if (reason !== undefined) {
|
|
74
|
+
_sessionErrors.set(name, { error: reason, ts: new Date() });
|
|
75
|
+
}
|
|
76
|
+
log.debug('hooks-guard', 'hook disabled', { name, reason: String(reason) });
|
|
77
|
+
}
|
|
78
|
+
/** 检查 hook 是否已被 session-level 禁用 */
|
|
79
|
+
export function isHookDisabled(name) {
|
|
80
|
+
return _sessionDisabled.has(name);
|
|
81
|
+
}
|
|
82
|
+
/** 拿 session-level 错误 Map(调试用) */
|
|
83
|
+
export function _getSessionErrors() {
|
|
84
|
+
return _sessionErrors;
|
|
85
|
+
}
|
|
86
|
+
/** 测试用:清空 session-level disable 状态 */
|
|
87
|
+
export function _resetSessionHookGuard() {
|
|
88
|
+
_sessionDisabled.clear();
|
|
89
|
+
_sessionErrors.clear();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* no-op hook(v1.12:safeCreateHook 禁用时返回的 stub)
|
|
93
|
+
*
|
|
94
|
+
* 签名匹配:所有 opencode hook 接受 (input, output) → Promise<void> | void
|
|
95
|
+
* no-op 不做事,类型断言成对应 hook
|
|
96
|
+
*/
|
|
97
|
+
function noOpHook(_name) {
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
99
|
+
return (async (..._args) => {
|
|
100
|
+
// no-op: 不 rethrow 不 log(v0.0.1 release 静默原则)
|
|
101
|
+
// 调试时: log.debug('hooks-guard', 'no-op hook called', { name: _name });
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* safeCreateHook(v1.12 推荐 API)
|
|
106
|
+
*
|
|
107
|
+
* 包装 hook factory:
|
|
108
|
+
* 1. isHookEnabled === false → 返回 no-op(hook 签名匹配,host 调用但不做事)
|
|
109
|
+
* 2. factory() 抛错 → log.debug + disableHook + 返回 no-op
|
|
110
|
+
* 3. 注册后 hook 抛错 → log.debug + disableHook + 不 rethrow
|
|
111
|
+
*
|
|
112
|
+
* @param name hook 名(用于 env var / session disable 追踪)
|
|
113
|
+
* @param factory 工厂函数(v0 模式:返回 hook impl;这里就是返回 impl 本身)
|
|
114
|
+
* @param config per-hook 配置(可选)
|
|
115
|
+
*/
|
|
116
|
+
export function safeCreateHook(name, factory, config) {
|
|
117
|
+
// 闸 1: 启用?
|
|
118
|
+
if (!isHookEnabled(name, config)) {
|
|
119
|
+
return noOpHook(name);
|
|
120
|
+
}
|
|
121
|
+
// 闸 2: factory 抛错?
|
|
122
|
+
let hookImpl;
|
|
123
|
+
try {
|
|
124
|
+
hookImpl = factory();
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
disableHook(name, err);
|
|
128
|
+
return noOpHook(name);
|
|
129
|
+
}
|
|
130
|
+
// 闸 3: 注册后 hook 抛错 → disable + 不 rethrow
|
|
131
|
+
return (async (input, output) => {
|
|
132
|
+
try {
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
134
|
+
await hookImpl(input, output);
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
disableHook(name, err);
|
|
138
|
+
// v0.0.1 release 静默:不 rethrow,不 console 输出
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* safeHook(v0.0.1 旧 API,向后兼容)
|
|
144
|
+
*
|
|
145
|
+
* 行为:
|
|
146
|
+
* - isHookEnabled === false → 返回 undefined(host 不注册 hook,走默认)
|
|
147
|
+
* - 注册后 hook 抛错 → v1.12: disableHook + 不 rethrow
|
|
148
|
+
*
|
|
149
|
+
* 与 safeCreateHook 区别:
|
|
150
|
+
* - safeCreateHook 返回 no-op(hook 已注册但不做事)
|
|
151
|
+
* - safeHook 返回 undefined(hook 未注册)
|
|
152
|
+
*
|
|
153
|
+
* 选择建议:
|
|
154
|
+
* - 旧代码(v0.1-3 起的 hook 工厂)继续用 safeHook,行为兼容
|
|
155
|
+
* - 新代码推荐 safeCreateHook,hook 仍注册(host 期望所有 hook 都存在)
|
|
156
|
+
*/
|
|
157
|
+
export function safeHook(name, impl, config) {
|
|
158
|
+
if (!isHookEnabled(name, config))
|
|
159
|
+
return undefined;
|
|
160
|
+
return (async (input, output) => {
|
|
161
|
+
try {
|
|
162
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
163
|
+
await impl(input, output);
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
// v0.0.1 release 静默:不 rethrow,不 console 输出
|
|
167
|
+
// v1.12: mark disabled for rest of session
|
|
168
|
+
disableHook(name, err);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/hooks/util.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* opencode-serenity-plugin — server entry
|
|
3
|
+
*
|
|
4
|
+
* 职责:注册 5 个自定义工具 + 6 个 system hook,实现 serenity 认知基础设施的
|
|
5
|
+
* plugin 层(MSM 框架、文件系统操作、会话管理、路径守卫、skill 注入等)。
|
|
6
|
+
*
|
|
7
|
+
* 设计文档见 docs/:
|
|
8
|
+
* - architecture-v0.md — 两阶段 init + 模块分解
|
|
9
|
+
* - contract-v0.md — 6 契约 + 13 错误类
|
|
10
|
+
* - requirements-v0-scope.md — RR1-RR7 范围层
|
|
11
|
+
*
|
|
12
|
+
* Hook 工厂分层:
|
|
13
|
+
* createPermissionGuards → tool.execute.before(RR5 路径守卫 + bash 开关)
|
|
14
|
+
* createCompactingHooks → system.transform(SKILL.md 注入)
|
|
15
|
+
* + session.compacting(关键状态保留)
|
|
16
|
+
* + tool.definition(subagent context 注入)
|
|
17
|
+
* createShellEnv → shell.env(HOME_SERENITY_ROOT + SERENITY_INSTANCE)
|
|
18
|
+
* createPermissionAutoReply → event permission.asked(cwdRoot 内自动 always)
|
|
19
|
+
*/
|
|
20
|
+
import type { Plugin } from '@opencode-ai/plugin';
|
|
21
|
+
declare const _default: {
|
|
22
|
+
id: string;
|
|
23
|
+
server: Plugin;
|
|
24
|
+
};
|
|
25
|
+
export default _default;
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAS,MAAM,qBAAqB,CAAC;;;;;AAoDzD,wBAGE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* opencode-serenity-plugin — server entry
|
|
3
|
+
*
|
|
4
|
+
* 职责:注册 5 个自定义工具 + 6 个 system hook,实现 serenity 认知基础设施的
|
|
5
|
+
* plugin 层(MSM 框架、文件系统操作、会话管理、路径守卫、skill 注入等)。
|
|
6
|
+
*
|
|
7
|
+
* 设计文档见 docs/:
|
|
8
|
+
* - architecture-v0.md — 两阶段 init + 模块分解
|
|
9
|
+
* - contract-v0.md — 6 契约 + 13 错误类
|
|
10
|
+
* - requirements-v0-scope.md — RR1-RR7 范围层
|
|
11
|
+
*
|
|
12
|
+
* Hook 工厂分层:
|
|
13
|
+
* createPermissionGuards → tool.execute.before(RR5 路径守卫 + bash 开关)
|
|
14
|
+
* createCompactingHooks → system.transform(SKILL.md 注入)
|
|
15
|
+
* + session.compacting(关键状态保留)
|
|
16
|
+
* + tool.definition(subagent context 注入)
|
|
17
|
+
* createShellEnv → shell.env(HOME_SERENITY_ROOT + SERENITY_INSTANCE)
|
|
18
|
+
* createPermissionAutoReply → event permission.asked(cwdRoot 内自动 always)
|
|
19
|
+
*/
|
|
20
|
+
import { tryActivateSync } from './activation.js';
|
|
21
|
+
import { msmListTool, msmExecTool, msmAdminTool, } from './msm.js';
|
|
22
|
+
import { fileSystemTool } from './fs/file-system-tool.js';
|
|
23
|
+
import { sessionTool } from './session/session-tool.js';
|
|
24
|
+
import { createPermissionGuards } from './hooks/permission-guards.js';
|
|
25
|
+
import { createCompactingHooks } from './hooks/compacting.js';
|
|
26
|
+
import { createShellEnv } from './hooks/shell-env.js';
|
|
27
|
+
import { createPermissionAutoReplyHandler } from './hooks/permission-auto-reply.js';
|
|
28
|
+
import { log } from './util/log.js';
|
|
29
|
+
const plugin = async (input) => {
|
|
30
|
+
log.info('entry', 'plugin loading', { directory: input.directory, worktree: input.worktree });
|
|
31
|
+
// Phase 1: 同步 RR6 验证(git repo)
|
|
32
|
+
const syncResult = tryActivateSync(input, () => input.client);
|
|
33
|
+
if (!syncResult.ok) {
|
|
34
|
+
log.warn('entry', 'plugin not activated', { reason: syncResult.reason });
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
// Phase 2 启动:fire-and-forget,状态机后台验证 RR1 + RR2
|
|
38
|
+
// (由 activation.activateAsync 内部触发,此处不 await)
|
|
39
|
+
// 注册 hooks + tools(Phase 2 未完成时 hook 内 await ensureReady() 阻塞)
|
|
40
|
+
const hooks = {
|
|
41
|
+
tool: {
|
|
42
|
+
msm_list: msmListTool,
|
|
43
|
+
msm_exec: msmExecTool,
|
|
44
|
+
msm_admin: msmAdminTool,
|
|
45
|
+
file_system: fileSystemTool, // v0.1 D4: 跨实例文件系统工具
|
|
46
|
+
session_tool: sessionTool, // v0.1 D5: 通用会话管理工具
|
|
47
|
+
},
|
|
48
|
+
...createPermissionGuards(),
|
|
49
|
+
...createCompactingHooks(),
|
|
50
|
+
...createShellEnv(),
|
|
51
|
+
event: createPermissionAutoReplyHandler({
|
|
52
|
+
getServerUrl: () => input.serverUrl,
|
|
53
|
+
}),
|
|
54
|
+
};
|
|
55
|
+
log.info('entry', 'phase 1 ok; phase 2 loading in background', { cwdRoot: syncResult.cwdRoot });
|
|
56
|
+
log.info('entry', 'registered tools', { tools: Object.keys(hooks.tool ?? {}) });
|
|
57
|
+
return hooks;
|
|
58
|
+
};
|
|
59
|
+
export default {
|
|
60
|
+
id: 'opencode-serenity-plugin-server',
|
|
61
|
+
server: plugin,
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EACL,WAAW,EACX,WAAW,EACX,YAAY,GAEb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,gCAAgC,EAAE,MAAM,kCAAkC,CAAC;AACpF,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAEpC,MAAM,MAAM,GAAW,KAAK,EAAE,KAAK,EAAE,EAAE;IACrC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IAE9F,+BAA+B;IAC/B,MAAM,UAAU,GAAG,eAAe,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAE9D,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC;QACnB,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,sBAAsB,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;QACzE,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,+CAA+C;IAC/C,8CAA8C;IAE9C,+DAA+D;IAC/D,MAAM,KAAK,GAAU;QACnB,IAAI,EAAE;YACJ,QAAQ,EAAE,WAAW;YACrB,QAAQ,EAAE,WAAW;YACrB,SAAS,EAAE,YAAY;YACvB,WAAW,EAAE,cAAc,EAAE,qBAAqB;YAClD,YAAY,EAAE,WAAW,EAAE,oBAAoB;SAChD;QACD,GAAG,sBAAsB,EAAE;QAC3B,GAAG,qBAAqB,EAAE;QAC1B,GAAG,cAAc,EAAE;QACnB,KAAK,EAAE,gCAAgC,CAAC;YACtC,YAAY,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,SAAS;SACpC,CAAC;KACH,CAAC;IAEF,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,2CAA2C,EAAE,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;IAChG,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IAChF,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,eAAe;IACb,EAAE,EAAE,iCAAiC;IACrC,MAAM,EAAE,MAAM;CACf,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* init-wizard.ts — D1 Init 向导 Phase 1
|
|
3
|
+
*
|
|
4
|
+
* CLI 交互式创建宁静号实例的目录骨架并复制标准技能模板。
|
|
5
|
+
*
|
|
6
|
+
* 流程:
|
|
7
|
+
* 1. 收集基本信息(prefix, description)
|
|
8
|
+
* 2. 创建目录骨架(.serenity, .gitignore, AGENT_SESSIONS/, docs/)
|
|
9
|
+
* 3. 复制 9 个标准技能模板到 .opencode/skills/
|
|
10
|
+
* 4. 生成 opencode.json(注册 plugin)
|
|
11
|
+
* 5. 写入 Phase 2 Agent prompt
|
|
12
|
+
* 6. 输出完成信息
|
|
13
|
+
*/
|
|
14
|
+
export interface InitOptions {
|
|
15
|
+
/** 目标路径 */
|
|
16
|
+
targetPath: string;
|
|
17
|
+
/** 实例前缀(如 home, tg, pg) */
|
|
18
|
+
prefix?: string;
|
|
19
|
+
/** 实例名称(如 宁静号,用于根 skill 标题) */
|
|
20
|
+
name?: string;
|
|
21
|
+
/** 实例描述(一句话) */
|
|
22
|
+
description?: string;
|
|
23
|
+
/** Plugin 根路径(用于定位模板) */
|
|
24
|
+
pluginRoot: string;
|
|
25
|
+
/** 非交互模式 */
|
|
26
|
+
nonInteractive?: boolean;
|
|
27
|
+
/** 强制覆盖已有文件 */
|
|
28
|
+
force?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface InitResult {
|
|
31
|
+
success: boolean;
|
|
32
|
+
prefix: string;
|
|
33
|
+
name: string;
|
|
34
|
+
message: string;
|
|
35
|
+
createdDirs: string[];
|
|
36
|
+
installedSkills: string[];
|
|
37
|
+
}
|
|
38
|
+
export declare function initWizard(opts: InitOptions): Promise<InitResult>;
|
|
39
|
+
//# sourceMappingURL=init-wizard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-wizard.d.ts","sourceRoot":"","sources":["../../src/init/init-wizard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAeH,MAAM,WAAW,WAAW;IAC1B,WAAW;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yBAAyB;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY;IACZ,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,eAAe;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAsMD,wBAAsB,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CA2HvE"}
|