@miniidealab/openlogos 0.9.31 → 0.10.3
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/claude-plugin-template/.claude-plugin/plugin.json +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +7 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/smoke.d.ts +3 -1
- package/dist/commands/smoke.d.ts.map +1 -1
- package/dist/commands/smoke.js +43 -9
- package/dist/commands/smoke.js.map +1 -1
- package/dist/commands/verify.d.ts +10 -1
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +128 -67
- package/dist/commands/verify.js.map +1 -1
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +4 -0
- package/dist/i18n.js.map +1 -1
- package/dist/lib/sandbox.d.ts +38 -0
- package/dist/lib/sandbox.d.ts.map +1 -0
- package/dist/lib/sandbox.js +246 -0
- package/dist/lib/sandbox.js.map +1 -0
- package/dist/lib/verify-config.d.ts +8 -0
- package/dist/lib/verify-config.d.ts.map +1 -1
- package/dist/lib/verify-config.js +29 -0
- package/dist/lib/verify-config.js.map +1 -1
- package/package.json +1 -1
- package/spec/cli-json-output.md +41 -0
- package/spec/logos.config.schema.json +44 -3
- package/spec/test-results.md +25 -0
- package/spec/workflow.md +56 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readdirSync, readlinkSync, rmSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, normalize, relative, resolve } from 'node:path';
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
const DEFAULT_SANDBOX_ROOT = '/private/tmp';
|
|
5
|
+
function asRecord(value) {
|
|
6
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
7
|
+
return null;
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
function commandExitCode(error) {
|
|
11
|
+
if (error && typeof error === 'object' && 'status' in error) {
|
|
12
|
+
const status = error.status;
|
|
13
|
+
return typeof status === 'number' ? status : undefined;
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
function commandErrorMessage(error) {
|
|
18
|
+
if (error instanceof Error && error.message)
|
|
19
|
+
return error.message.slice(0, 500);
|
|
20
|
+
if (error !== undefined && error !== null)
|
|
21
|
+
return String(error).slice(0, 500);
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
function normalizeRelPath(path) {
|
|
25
|
+
return normalize(path).replaceAll('\\', '/').replace(/^\.\/+/, '').replace(/^\/+/, '');
|
|
26
|
+
}
|
|
27
|
+
function normalizeAllowedWriteSet(paths) {
|
|
28
|
+
const set = new Set();
|
|
29
|
+
for (const item of paths) {
|
|
30
|
+
const normalized = normalizeRelPath(item);
|
|
31
|
+
if (normalized)
|
|
32
|
+
set.add(normalized);
|
|
33
|
+
}
|
|
34
|
+
return set;
|
|
35
|
+
}
|
|
36
|
+
function listFileSnapshots(root) {
|
|
37
|
+
const snapshots = new Map();
|
|
38
|
+
if (!existsSync(root))
|
|
39
|
+
return snapshots;
|
|
40
|
+
const walk = (dir) => {
|
|
41
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
const fullPath = join(dir, entry.name);
|
|
44
|
+
const rel = normalizeRelPath(relative(root, fullPath));
|
|
45
|
+
if (!rel)
|
|
46
|
+
continue;
|
|
47
|
+
const stats = lstatSync(fullPath);
|
|
48
|
+
if (stats.isDirectory()) {
|
|
49
|
+
walk(fullPath);
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (stats.isSymbolicLink()) {
|
|
53
|
+
snapshots.set(rel, `l:${readlinkSync(fullPath)}`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
snapshots.set(rel, `f:${stats.size}:${Math.floor(stats.mtimeMs)}`);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
walk(root);
|
|
60
|
+
return snapshots;
|
|
61
|
+
}
|
|
62
|
+
function collectChangedPaths(before, after) {
|
|
63
|
+
const changed = new Set();
|
|
64
|
+
for (const [path, signature] of before.entries()) {
|
|
65
|
+
const next = after.get(path);
|
|
66
|
+
if (next === undefined || next !== signature)
|
|
67
|
+
changed.add(path);
|
|
68
|
+
}
|
|
69
|
+
for (const path of after.keys()) {
|
|
70
|
+
if (!before.has(path))
|
|
71
|
+
changed.add(path);
|
|
72
|
+
}
|
|
73
|
+
return Array.from(changed).sort();
|
|
74
|
+
}
|
|
75
|
+
function isPathAllowed(path, allowed) {
|
|
76
|
+
if (allowed.has(path))
|
|
77
|
+
return true;
|
|
78
|
+
for (const allow of allowed) {
|
|
79
|
+
if (!allow)
|
|
80
|
+
continue;
|
|
81
|
+
if (path.startsWith(`${allow}/`))
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
function copyBackAllowedFiles(sandboxRoot, workspaceRoot, changedPaths, allowedPaths) {
|
|
87
|
+
for (const relPath of changedPaths) {
|
|
88
|
+
if (!isPathAllowed(relPath, allowedPaths))
|
|
89
|
+
continue;
|
|
90
|
+
const src = join(sandboxRoot, relPath);
|
|
91
|
+
const dest = join(workspaceRoot, relPath);
|
|
92
|
+
if (!existsSync(src))
|
|
93
|
+
continue;
|
|
94
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
95
|
+
cpSync(src, dest, { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function executeCommand(cwd, command, format) {
|
|
99
|
+
const start = Date.now();
|
|
100
|
+
try {
|
|
101
|
+
execSync(command, { cwd, stdio: format === 'json' ? 'pipe' : 'inherit' });
|
|
102
|
+
return {
|
|
103
|
+
status: 'pass',
|
|
104
|
+
exit_code: 0,
|
|
105
|
+
duration_ms: Date.now() - start,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
return {
|
|
110
|
+
status: 'fail',
|
|
111
|
+
exit_code: commandExitCode(error),
|
|
112
|
+
duration_ms: Date.now() - start,
|
|
113
|
+
error: commandErrorMessage(error),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function makeUnsupportedSandboxResult(format, root, command, config, error) {
|
|
118
|
+
const message = commandErrorMessage(error) ?? 'sandbox initialization failed';
|
|
119
|
+
const diagnostics = [`无法启用沙箱:${message}`];
|
|
120
|
+
if (config.mode === 'always') {
|
|
121
|
+
return {
|
|
122
|
+
command: {
|
|
123
|
+
status: 'fail',
|
|
124
|
+
error: `sandbox_mode=always requires isolation, but setup failed: ${message}`,
|
|
125
|
+
},
|
|
126
|
+
sandbox: {
|
|
127
|
+
mode: config.mode,
|
|
128
|
+
root: config.root,
|
|
129
|
+
isolated: false,
|
|
130
|
+
workspace_write_denied: config.denyWorkspaceWrite,
|
|
131
|
+
status: 'fail',
|
|
132
|
+
diagnostics,
|
|
133
|
+
suggestions: ['检查 sandbox_root 是否可写,或修复当前运行环境的隔离能力。'],
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const plain = executeCommand(root, command, format);
|
|
138
|
+
return {
|
|
139
|
+
command: plain,
|
|
140
|
+
sandbox: {
|
|
141
|
+
mode: config.mode,
|
|
142
|
+
root: config.root,
|
|
143
|
+
isolated: false,
|
|
144
|
+
workspace_write_denied: config.denyWorkspaceWrite,
|
|
145
|
+
status: 'warn',
|
|
146
|
+
diagnostics,
|
|
147
|
+
suggestions: ['当前按兼容模式执行。若需强制隔离,请将 sandbox_mode 设为 always。'],
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
export function normalizeSandboxConfig(raw) {
|
|
152
|
+
const record = asRecord(raw) ?? {};
|
|
153
|
+
const modeRaw = typeof record.sandbox_mode === 'string' ? record.sandbox_mode : 'off';
|
|
154
|
+
const mode = modeRaw === 'auto' || modeRaw === 'always' ? modeRaw : 'off';
|
|
155
|
+
const root = typeof record.sandbox_root === 'string' && record.sandbox_root.trim().length > 0
|
|
156
|
+
? record.sandbox_root
|
|
157
|
+
: DEFAULT_SANDBOX_ROOT;
|
|
158
|
+
const denyWorkspaceWrite = typeof record.sandbox_deny_workspace_write === 'boolean'
|
|
159
|
+
? record.sandbox_deny_workspace_write
|
|
160
|
+
: true;
|
|
161
|
+
return { mode, root, denyWorkspaceWrite };
|
|
162
|
+
}
|
|
163
|
+
export function buildInitialSandboxData(config) {
|
|
164
|
+
return {
|
|
165
|
+
mode: config.mode,
|
|
166
|
+
root: config.root,
|
|
167
|
+
isolated: false,
|
|
168
|
+
workspace_write_denied: config.denyWorkspaceWrite,
|
|
169
|
+
status: 'skipped',
|
|
170
|
+
diagnostics: [],
|
|
171
|
+
suggestions: [],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
export function runSandboxedCommand(options) {
|
|
175
|
+
const { root, command, format, sandbox, allowedWritePaths } = options;
|
|
176
|
+
const normalizedRoot = resolve(root);
|
|
177
|
+
if (sandbox.mode === 'off') {
|
|
178
|
+
return {
|
|
179
|
+
command: executeCommand(normalizedRoot, command, format),
|
|
180
|
+
sandbox: {
|
|
181
|
+
...buildInitialSandboxData(sandbox),
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
let sandboxBase = sandbox.root;
|
|
186
|
+
if (!sandboxBase || sandboxBase.trim().length === 0) {
|
|
187
|
+
sandboxBase = DEFAULT_SANDBOX_ROOT;
|
|
188
|
+
}
|
|
189
|
+
let sandboxDir = '';
|
|
190
|
+
let sandboxProjectRoot = '';
|
|
191
|
+
try {
|
|
192
|
+
mkdirSync(sandboxBase, { recursive: true });
|
|
193
|
+
sandboxDir = mkdtempSync(join(sandboxBase, 'openlogos-cli-sandbox-'));
|
|
194
|
+
sandboxProjectRoot = join(sandboxDir, 'workspace');
|
|
195
|
+
cpSync(normalizedRoot, sandboxProjectRoot, { recursive: true });
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
if (sandboxDir && existsSync(sandboxDir)) {
|
|
199
|
+
rmSync(sandboxDir, { recursive: true, force: true });
|
|
200
|
+
}
|
|
201
|
+
return makeUnsupportedSandboxResult(format, normalizedRoot, command, sandbox, error);
|
|
202
|
+
}
|
|
203
|
+
const baseline = listFileSnapshots(sandboxProjectRoot);
|
|
204
|
+
const commandResult = executeCommand(sandboxProjectRoot, command, format);
|
|
205
|
+
const afterRun = listFileSnapshots(sandboxProjectRoot);
|
|
206
|
+
const changedPaths = collectChangedPaths(baseline, afterRun);
|
|
207
|
+
const allowedSet = normalizeAllowedWriteSet(allowedWritePaths);
|
|
208
|
+
const unauthorizedWrites = sandbox.denyWorkspaceWrite
|
|
209
|
+
? changedPaths.filter(path => !isPathAllowed(path, allowedSet))
|
|
210
|
+
: [];
|
|
211
|
+
copyBackAllowedFiles(sandboxProjectRoot, normalizedRoot, changedPaths, allowedSet);
|
|
212
|
+
const sandboxData = {
|
|
213
|
+
mode: sandbox.mode,
|
|
214
|
+
root: sandboxDir,
|
|
215
|
+
isolated: true,
|
|
216
|
+
workspace_write_denied: sandbox.denyWorkspaceWrite,
|
|
217
|
+
status: 'pass',
|
|
218
|
+
diagnostics: [],
|
|
219
|
+
suggestions: [],
|
|
220
|
+
};
|
|
221
|
+
if (unauthorizedWrites.length > 0) {
|
|
222
|
+
const preview = unauthorizedWrites.slice(0, 5).join(', ');
|
|
223
|
+
sandboxData.diagnostics.push(`检测到非白名单写入:${preview}${unauthorizedWrites.length > 5 ? ' ...' : ''}`);
|
|
224
|
+
if (sandbox.mode === 'always') {
|
|
225
|
+
sandboxData.status = 'fail';
|
|
226
|
+
sandboxData.suggestions.push('仅允许写入结果文件白名单;请调整测试脚本输出目录。');
|
|
227
|
+
commandResult.status = 'fail';
|
|
228
|
+
if (!commandResult.error) {
|
|
229
|
+
commandResult.error = 'sandbox_mode=always blocked non-whitelist writes';
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
sandboxData.status = 'warn';
|
|
234
|
+
sandboxData.suggestions.push('当前按兼容策略继续。若需强制阻断,请将 sandbox_mode 设为 always。');
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (commandResult.status === 'fail' && sandboxData.status === 'pass') {
|
|
238
|
+
sandboxData.diagnostics.push('命令在沙箱内执行失败,请检查 pre-run / smoke 命令输出。');
|
|
239
|
+
}
|
|
240
|
+
if (sandboxData.status === 'warn' && sandboxData.suggestions.length === 0) {
|
|
241
|
+
sandboxData.suggestions.push('如需严格隔离,请将 sandbox_mode 设为 always。');
|
|
242
|
+
}
|
|
243
|
+
rmSync(sandboxDir, { recursive: true, force: true });
|
|
244
|
+
return { command: commandResult, sandbox: sandboxData };
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/lib/sandbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { type NormalizedSandboxConfig } from './sandbox.js';
|
|
1
2
|
export declare const DEFAULT_VERIFY_RESULT_PATH = "logos/resources/verify/test-results.jsonl";
|
|
2
3
|
export declare const DEFAULT_VERIFY_MERGE_STRATEGY = "last-write-wins";
|
|
4
|
+
export declare const DEFAULT_SANDBOX_MODE = "auto";
|
|
5
|
+
export declare const DEFAULT_SANDBOX_ROOT = "/private/tmp";
|
|
6
|
+
export declare const DEFAULT_SANDBOX_DENY_WORKSPACE_WRITE = true;
|
|
3
7
|
export interface VerifyConfig {
|
|
4
8
|
result_path?: string;
|
|
5
9
|
pre_run_command?: string;
|
|
@@ -9,6 +13,9 @@ export interface VerifyConfig {
|
|
|
9
13
|
regression_result_path?: string;
|
|
10
14
|
incremental_result_path?: string;
|
|
11
15
|
merge_results?: string;
|
|
16
|
+
sandbox_mode?: 'off' | 'auto' | 'always';
|
|
17
|
+
sandbox_root?: string;
|
|
18
|
+
sandbox_deny_workspace_write?: boolean;
|
|
12
19
|
}
|
|
13
20
|
export interface NormalizedVerifyConfig {
|
|
14
21
|
resultPath: string;
|
|
@@ -18,6 +25,7 @@ export interface NormalizedVerifyConfig {
|
|
|
18
25
|
regressionResultPath?: string;
|
|
19
26
|
incrementalResultPath?: string;
|
|
20
27
|
mergeStrategy: 'last-write-wins';
|
|
28
|
+
sandbox: NormalizedSandboxConfig;
|
|
21
29
|
}
|
|
22
30
|
export interface VerifyPreRunBackfillResult {
|
|
23
31
|
status: 'exists' | 'added' | 'todo';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-config.d.ts","sourceRoot":"","sources":["../../src/lib/verify-config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"verify-config.d.ts","sourceRoot":"","sources":["../../src/lib/verify-config.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0B,KAAK,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEpF,eAAO,MAAM,0BAA0B,8CAA8C,CAAC;AACtF,eAAO,MAAM,6BAA6B,oBAAoB,CAAC;AAC/D,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAC3C,eAAO,MAAM,oBAAoB,iBAAiB,CAAC;AACnD,eAAO,MAAM,oCAAoC,OAAO,CAAC;AAEzD,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4BAA4B,CAAC,EAAE,OAAO,CAAC;CACxC;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,aAAa,EAAE,iBAAiB,CAAC;IACjC,OAAO,EAAE,uBAAuB,CAAC;CAClC;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;CAClB;AAsCD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAwBpE;AAED,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,OAAO,GAAG,sBAAsB,CAiBhF;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,sBAAsB,CAGrE;AAED,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAGjE;AAmBD,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,0BAA0B,CA2BpH"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
+
import { normalizeSandboxConfig } from './sandbox.js';
|
|
3
4
|
export const DEFAULT_VERIFY_RESULT_PATH = 'logos/resources/verify/test-results.jsonl';
|
|
4
5
|
export const DEFAULT_VERIFY_MERGE_STRATEGY = 'last-write-wins';
|
|
6
|
+
export const DEFAULT_SANDBOX_MODE = 'auto';
|
|
7
|
+
export const DEFAULT_SANDBOX_ROOT = '/private/tmp';
|
|
8
|
+
export const DEFAULT_SANDBOX_DENY_WORKSPACE_WRITE = true;
|
|
5
9
|
function readJsonFile(path) {
|
|
6
10
|
try {
|
|
7
11
|
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
@@ -74,6 +78,7 @@ export function normalizeVerifyConfig(rawVerify) {
|
|
|
74
78
|
regressionResultPath: typeof verify.regression_result_path === 'string' ? verify.regression_result_path : undefined,
|
|
75
79
|
incrementalResultPath: typeof verify.incremental_result_path === 'string' ? verify.incremental_result_path : undefined,
|
|
76
80
|
mergeStrategy,
|
|
81
|
+
sandbox: normalizeSandboxConfig(verify),
|
|
77
82
|
};
|
|
78
83
|
}
|
|
79
84
|
export function readVerifyConfig(root) {
|
|
@@ -84,6 +89,22 @@ export function hasVerifyPreRunConfig(rawVerify) {
|
|
|
84
89
|
const verify = normalizeVerifyConfig(rawVerify);
|
|
85
90
|
return Boolean(verify.preRunCommand || verify.regressionCommand || verify.incrementalCommand);
|
|
86
91
|
}
|
|
92
|
+
function backfillSandboxDefaults(target) {
|
|
93
|
+
let changed = false;
|
|
94
|
+
if (target.sandbox_mode === undefined) {
|
|
95
|
+
target.sandbox_mode = DEFAULT_SANDBOX_MODE;
|
|
96
|
+
changed = true;
|
|
97
|
+
}
|
|
98
|
+
if (target.sandbox_root === undefined) {
|
|
99
|
+
target.sandbox_root = DEFAULT_SANDBOX_ROOT;
|
|
100
|
+
changed = true;
|
|
101
|
+
}
|
|
102
|
+
if (target.sandbox_deny_workspace_write === undefined) {
|
|
103
|
+
target.sandbox_deny_workspace_write = DEFAULT_SANDBOX_DENY_WORKSPACE_WRITE;
|
|
104
|
+
changed = true;
|
|
105
|
+
}
|
|
106
|
+
return changed;
|
|
107
|
+
}
|
|
87
108
|
export function backfillVerifyPreRunConfig(root, config) {
|
|
88
109
|
const verify = asRecord(config.verify) ?? {};
|
|
89
110
|
let mutated = false;
|
|
@@ -91,7 +112,15 @@ export function backfillVerifyPreRunConfig(root, config) {
|
|
|
91
112
|
verify.result_path = DEFAULT_VERIFY_RESULT_PATH;
|
|
92
113
|
mutated = true;
|
|
93
114
|
}
|
|
115
|
+
if (backfillSandboxDefaults(verify)) {
|
|
116
|
+
mutated = true;
|
|
117
|
+
}
|
|
94
118
|
config.verify = verify;
|
|
119
|
+
const smoke = asRecord(config.smoke) ?? {};
|
|
120
|
+
if (backfillSandboxDefaults(smoke)) {
|
|
121
|
+
mutated = true;
|
|
122
|
+
}
|
|
123
|
+
config.smoke = smoke;
|
|
95
124
|
if (hasVerifyPreRunConfig(verify)) {
|
|
96
125
|
return { status: 'exists', mutated };
|
|
97
126
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-config.js","sourceRoot":"","sources":["../../src/lib/verify-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"verify-config.js","sourceRoot":"","sources":["../../src/lib/verify-config.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/spec/cli-json-output.md
CHANGED
|
@@ -361,6 +361,15 @@ openlogos verify --format json # JSON 格式
|
|
|
361
361
|
"diagnostics": [],
|
|
362
362
|
"suggestions": []
|
|
363
363
|
},
|
|
364
|
+
"sandbox": {
|
|
365
|
+
"mode": "auto", // "off" | "auto" | "always"
|
|
366
|
+
"root": "/private/tmp",
|
|
367
|
+
"isolated": true,
|
|
368
|
+
"workspace_write_denied": true,
|
|
369
|
+
"status": "pass", // "pass" | "warn" | "fail" | "skipped"
|
|
370
|
+
"diagnostics": [],
|
|
371
|
+
"suggestions": []
|
|
372
|
+
},
|
|
364
373
|
"report_path": "logos/resources/verify/acceptance-report.md"
|
|
365
374
|
}
|
|
366
375
|
```
|
|
@@ -403,6 +412,13 @@ openlogos verify --format json # JSON 格式
|
|
|
403
412
|
| `pre_run.merge_strategy` | string \| null | 否 | 两阶段合并策略,当前为 `last-write-wins` |
|
|
404
413
|
| `pre_run.diagnostics` | string[] | 是 | 可展示给用户的问题诊断 |
|
|
405
414
|
| `pre_run.suggestions` | string[] | 是 | 可展示给用户的修复建议 |
|
|
415
|
+
| `sandbox.mode` | string | 是 | verify 沙箱模式:`"off"`、`"auto"` 或 `"always"` |
|
|
416
|
+
| `sandbox.root` | string | 是 | 沙箱根目录 |
|
|
417
|
+
| `sandbox.isolated` | boolean | 是 | 本次执行是否实际隔离 |
|
|
418
|
+
| `sandbox.workspace_write_denied` | boolean | 是 | 是否拒绝写入仓库工作区 |
|
|
419
|
+
| `sandbox.status` | string | 是 | 沙箱执行结果 |
|
|
420
|
+
| `sandbox.diagnostics` | string[] | 是 | 沙箱诊断信息 |
|
|
421
|
+
| `sandbox.suggestions` | string[] | 是 | 沙箱修复建议 |
|
|
406
422
|
| `report_path` | string | 是 | 生成的验收报告路径 |
|
|
407
423
|
|
|
408
424
|
### 4.4 gate.reason 取值
|
|
@@ -420,6 +436,9 @@ openlogos verify --format json # JSON 格式
|
|
|
420
436
|
- 旧项目只配置 `verify.pre_run_command` 时,`pre_run.mode="pre_run_command"`。
|
|
421
437
|
- 配置 `verify.regression_command` 或 `verify.incremental_command` 时,`pre_run.mode="two_phase"`。
|
|
422
438
|
- 没有任何预跑命令时,`pre_run.mode="none"`。
|
|
439
|
+
- `sandbox_mode="off"` 时,`sandbox.status="skipped"`,并保持历史兼容行为。
|
|
440
|
+
- `sandbox_mode="auto"` 时,环境支持隔离则 `sandbox.status="pass"`,不支持则 `sandbox.status="warn"` 并给出降级原因。
|
|
441
|
+
- `sandbox_mode="always"` 时,若无法隔离则必须失败。
|
|
423
442
|
- 覆盖不足且 `pre_run.mode="none"` 时,必须输出局部测试诊断和配置建议。
|
|
424
443
|
|
|
425
444
|
---
|
|
@@ -459,6 +478,15 @@ openlogos smoke --env production --format json
|
|
|
459
478
|
"failed_cases": [],
|
|
460
479
|
"uncovered_cases": [],
|
|
461
480
|
"skipped_cases": [],
|
|
481
|
+
"sandbox": {
|
|
482
|
+
"mode": "auto", // "off" | "auto" | "always"
|
|
483
|
+
"root": "/private/tmp",
|
|
484
|
+
"isolated": true,
|
|
485
|
+
"workspace_write_denied": true,
|
|
486
|
+
"status": "pass", // "pass" | "warn" | "fail" | "skipped"
|
|
487
|
+
"diagnostics": [],
|
|
488
|
+
"suggestions": []
|
|
489
|
+
},
|
|
462
490
|
"report_path": "logos/resources/verify/smoke-report.md",
|
|
463
491
|
"result_path": "logos/resources/verify/smoke-results.jsonl"
|
|
464
492
|
}
|
|
@@ -475,11 +503,24 @@ openlogos smoke --env production --format json
|
|
|
475
503
|
| `gate.reason` | string \| null | 是 | 失败原因,如 `failed_cases` / `incomplete_coverage` |
|
|
476
504
|
| `failed_cases` | array | 是 | 失败 smoke 用例 |
|
|
477
505
|
| `uncovered_cases` | array | 是 | 未覆盖 smoke 用例 ID |
|
|
506
|
+
| `sandbox.mode` | string | 是 | smoke 沙箱模式:`"off"`、`"auto"` 或 `"always"` |
|
|
507
|
+
| `sandbox.root` | string | 是 | 沙箱根目录 |
|
|
508
|
+
| `sandbox.isolated` | boolean | 是 | 本次执行是否实际隔离 |
|
|
509
|
+
| `sandbox.workspace_write_denied` | boolean | 是 | 是否拒绝写入仓库工作区 |
|
|
510
|
+
| `sandbox.status` | string | 是 | 沙箱执行结果 |
|
|
511
|
+
| `sandbox.diagnostics` | string[] | 是 | 沙箱诊断信息 |
|
|
512
|
+
| `sandbox.suggestions` | string[] | 是 | 沙箱修复建议 |
|
|
478
513
|
| `report_path` | string | 是 | smoke 报告路径 |
|
|
479
514
|
| `result_path` | string | 是 | smoke 结果路径 |
|
|
480
515
|
|
|
481
516
|
`openlogos smoke` 与 `openlogos verify` 共享 JSONL 结果思想,但读取的是 `smoke.result_path`,默认 `logos/resources/verify/smoke-results.jsonl`。冒烟测试用例建议存放在 `logos/resources/test/smoke/`。
|
|
482
517
|
|
|
518
|
+
### 5.4 兼容规则
|
|
519
|
+
|
|
520
|
+
- `smoke.command` 仍按既有语义执行。
|
|
521
|
+
- `sandbox_mode` / `sandbox_root` / `sandbox_deny_workspace_write` 仅影响执行环境,不改变 smoke 门禁定义。
|
|
522
|
+
- 当沙箱失败时,`smoke` 仍应写入结果报告,但 JSON 输出必须明确失败原因。
|
|
523
|
+
|
|
483
524
|
---
|
|
484
525
|
|
|
485
526
|
## 6. 错误处理
|
|
@@ -79,6 +79,22 @@
|
|
|
79
79
|
"default": "last-write-wins",
|
|
80
80
|
"description": "两阶段结果合并策略。同一用例 ID 多次出现时,最后一次结果生效"
|
|
81
81
|
},
|
|
82
|
+
"sandbox_mode": {
|
|
83
|
+
"type": "string",
|
|
84
|
+
"enum": ["off", "auto", "always"],
|
|
85
|
+
"default": "off",
|
|
86
|
+
"description": "verify 沙箱执行模式;auto 表示默认隔离,环境不支持时可降级并告警,always 表示必须隔离"
|
|
87
|
+
},
|
|
88
|
+
"sandbox_root": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"default": "/private/tmp",
|
|
91
|
+
"description": "verify 沙箱工作根目录。沙箱执行器应在此目录内运行命令"
|
|
92
|
+
},
|
|
93
|
+
"sandbox_deny_workspace_write": {
|
|
94
|
+
"type": "boolean",
|
|
95
|
+
"default": true,
|
|
96
|
+
"description": "是否禁止 verify 预跑命令写入仓库工作区;true 时必须阻断工作区写入"
|
|
97
|
+
},
|
|
82
98
|
"test_command": {
|
|
83
99
|
"type": "string",
|
|
84
100
|
"description": "旧字段,保留兼容。建议使用 pre_run_command"
|
|
@@ -102,6 +118,22 @@
|
|
|
102
118
|
"type": "string",
|
|
103
119
|
"default": "logos/resources/verify/smoke-report.md",
|
|
104
120
|
"description": "冒烟测试报告输出路径(相对项目根目录)"
|
|
121
|
+
},
|
|
122
|
+
"sandbox_mode": {
|
|
123
|
+
"type": "string",
|
|
124
|
+
"enum": ["off", "auto", "always"],
|
|
125
|
+
"default": "off",
|
|
126
|
+
"description": "smoke 沙箱执行模式;auto 表示默认隔离,环境不支持时可降级并告警,always 表示必须隔离"
|
|
127
|
+
},
|
|
128
|
+
"sandbox_root": {
|
|
129
|
+
"type": "string",
|
|
130
|
+
"default": "/private/tmp",
|
|
131
|
+
"description": "smoke 沙箱工作根目录。沙箱执行器应在此目录内运行命令"
|
|
132
|
+
},
|
|
133
|
+
"sandbox_deny_workspace_write": {
|
|
134
|
+
"type": "boolean",
|
|
135
|
+
"default": true,
|
|
136
|
+
"description": "是否禁止 smoke 命令写入仓库工作区;true 时必须阻断工作区写入"
|
|
105
137
|
}
|
|
106
138
|
}
|
|
107
139
|
},
|
|
@@ -205,12 +237,18 @@
|
|
|
205
237
|
},
|
|
206
238
|
"verify": {
|
|
207
239
|
"result_path": "logos/resources/verify/test-results.jsonl",
|
|
208
|
-
"pre_run_command": "npm test"
|
|
240
|
+
"pre_run_command": "npm test",
|
|
241
|
+
"sandbox_mode": "auto",
|
|
242
|
+
"sandbox_root": "/private/tmp",
|
|
243
|
+
"sandbox_deny_workspace_write": true
|
|
209
244
|
},
|
|
210
245
|
"smoke": {
|
|
211
246
|
"command": "npm run smoke",
|
|
212
247
|
"result_path": "logos/resources/verify/smoke-results.jsonl",
|
|
213
|
-
"report_path": "logos/resources/verify/smoke-report.md"
|
|
248
|
+
"report_path": "logos/resources/verify/smoke-report.md",
|
|
249
|
+
"sandbox_mode": "auto",
|
|
250
|
+
"sandbox_root": "/private/tmp",
|
|
251
|
+
"sandbox_deny_workspace_write": true
|
|
214
252
|
}
|
|
215
253
|
},
|
|
216
254
|
{
|
|
@@ -222,7 +260,10 @@
|
|
|
222
260
|
"incremental_command": "npm run test:changed",
|
|
223
261
|
"regression_result_path": "logos/resources/verify/test-results.regression.jsonl",
|
|
224
262
|
"incremental_result_path": "logos/resources/verify/test-results.incremental.jsonl",
|
|
225
|
-
"merge_results": "last-write-wins"
|
|
263
|
+
"merge_results": "last-write-wins",
|
|
264
|
+
"sandbox_mode": "auto",
|
|
265
|
+
"sandbox_root": "/private/tmp",
|
|
266
|
+
"sandbox_deny_workspace_write": true
|
|
226
267
|
}
|
|
227
268
|
}
|
|
228
269
|
]
|
package/spec/test-results.md
CHANGED
|
@@ -138,6 +138,20 @@ logos/resources/verify/test-results.jsonl
|
|
|
138
138
|
4. 同一个用例 ID 多次出现时,最后一次结果生效。
|
|
139
139
|
5. 若未配置阶段结果路径,CLI 必须用临时快照或等价机制避免第二阶段 reporter 清空第一阶段结果。
|
|
140
140
|
|
|
141
|
+
### `verify.sandbox_mode` / `smoke.sandbox_mode`
|
|
142
|
+
|
|
143
|
+
OpenLogos 的 JSONL reporter 仍然负责写入测试结果,但当 `verify` 或 `smoke` 开启沙箱模式时,reporter 和命令执行器的职责必须分离:
|
|
144
|
+
|
|
145
|
+
- reporter 只写配置声明的结果文件;
|
|
146
|
+
- CLI 负责在沙箱中执行测试命令;
|
|
147
|
+
- CLI 负责回收结果文件并阻断仓库工作区的额外写入。
|
|
148
|
+
|
|
149
|
+
### 新增约束
|
|
150
|
+
- `verify.sandbox_mode="always"` 时,测试命令不得直接写入仓库根目录中的非白名单路径。
|
|
151
|
+
- `smoke.sandbox_mode="always"` 时,`smoke.command` 不得直接写入仓库根目录中的非白名单路径。
|
|
152
|
+
- 若测试脚本在沙箱内失败,CLI 必须保留失败输出,并在 JSON / 文本结果中暴露 `sandbox` 诊断。
|
|
153
|
+
- 阶段化结果路径(`regression_result_path` / `incremental_result_path`)与沙箱隔离可以同时存在,二者不冲突。
|
|
154
|
+
|
|
141
155
|
### 覆盖不足诊断
|
|
142
156
|
|
|
143
157
|
当项目没有配置 `pre_run_command`、`regression_command` 或 `incremental_command`,且 verify 发现覆盖不足时,CLI 必须诊断这可能是只运行了局部测试导致,并建议配置 verify 预跑命令。
|
|
@@ -153,6 +167,17 @@ logos/resources/verify/test-results.jsonl
|
|
|
153
167
|
|
|
154
168
|
两阶段模型下,回归阶段和增量阶段可以写入不同结果文件;如果两个阶段都写入默认 `result_path`,CLI 必须在阶段之间保留快照,确保最终合并结果不会丢失第一阶段覆盖。
|
|
155
169
|
|
|
170
|
+
### reporter 输出建议
|
|
171
|
+
```json
|
|
172
|
+
{"id":"UT-S13-01","status":"pass","duration_ms":12,"timestamp":"2026-04-10T10:00:00Z"}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
当启用沙箱时,reporter 不需要知道沙箱实现细节,但可在错误消息中保留命令上下文,便于 CLI 汇总诊断。
|
|
176
|
+
|
|
177
|
+
### 兼容性
|
|
178
|
+
- 现有 reporter 仍然按原格式工作,不需要新增字段。
|
|
179
|
+
- CLI 新增的沙箱执行器不得改变 `test-results.jsonl` 的基本 schema。
|
|
180
|
+
|
|
156
181
|
### 目录创建
|
|
157
182
|
|
|
158
183
|
reporter 在写入前应确保 `logos/resources/verify/` 目录存在(`mkdir -p` 等效操作)。
|
package/spec/workflow.md
CHANGED
|
@@ -294,6 +294,62 @@ AI 面前已有完整上下文(原型 + 场景 + API + DB + 部署方案 + 测
|
|
|
294
294
|
- Step 6 不承担“补写测试代码”的职责;其职责是对已完成的 Step 5 产物做自动化判定
|
|
295
295
|
- 项目应配置 verify 预跑命令;若无法推断,必须在交付说明中明确风险与手动配置方式
|
|
296
296
|
|
|
297
|
+
**verify / smoke 沙箱执行标准**:
|
|
298
|
+
|
|
299
|
+
`openlogos verify` 与 `openlogos smoke` 的命令执行属于运行时安全边界,不能只依赖 prompt 约束。CLI 必须支持在 `logos.config.json` 中配置沙箱策略,并在执行前置测试命令或 `smoke.command` 时按策略处理。
|
|
300
|
+
|
|
301
|
+
配置入口:
|
|
302
|
+
|
|
303
|
+
```json
|
|
304
|
+
{
|
|
305
|
+
"verify": {
|
|
306
|
+
"sandbox_mode": "auto",
|
|
307
|
+
"sandbox_root": "/private/tmp",
|
|
308
|
+
"sandbox_deny_workspace_write": true
|
|
309
|
+
},
|
|
310
|
+
"smoke": {
|
|
311
|
+
"sandbox_mode": "auto",
|
|
312
|
+
"sandbox_root": "/private/tmp",
|
|
313
|
+
"sandbox_deny_workspace_write": true
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
沙箱模式:
|
|
319
|
+
|
|
320
|
+
| 模式 | 行为 |
|
|
321
|
+
|------|------|
|
|
322
|
+
| `off` | 关闭沙箱,保持历史行为 |
|
|
323
|
+
| `auto` | 优先使用沙箱;环境不支持时降级并输出告警 |
|
|
324
|
+
| `always` | 必须使用沙箱;无法隔离或检测到工作区写入时直接失败 |
|
|
325
|
+
|
|
326
|
+
verify 顺序:
|
|
327
|
+
1. 读取 `verify.sandbox_*` 与预跑命令配置。
|
|
328
|
+
2. 若配置 `regression_command` / `incremental_command`,按两阶段模型执行。
|
|
329
|
+
3. 若只配置 `pre_run_command`,按单阶段模型执行。
|
|
330
|
+
4. 若 `sandbox_mode != "off"`,所有预跑命令必须通过沙箱执行器运行。
|
|
331
|
+
5. 执行结束后,仅允许回收配置声明的结果文件到 `verify.result_path`、`verify.regression_result_path` 或 `verify.incremental_result_path`。
|
|
332
|
+
6. 读取结果并计算 Gate 3.5。
|
|
333
|
+
7. 文本与 JSON 输出必须暴露沙箱模式、是否隔离、失败原因和修复建议。
|
|
334
|
+
|
|
335
|
+
smoke 顺序:
|
|
336
|
+
1. 完成提案级部署决策、`DEPLOY_DONE` 与 smoke 门禁检查。
|
|
337
|
+
2. 读取 `smoke.sandbox_*` 与 `smoke.command`。
|
|
338
|
+
3. 若配置 `smoke.command` 且 `sandbox_mode != "off"`,通过沙箱执行器运行。
|
|
339
|
+
4. 执行结束后,仅允许回收 `smoke.result_path` 指向的结果文件。
|
|
340
|
+
5. 读取 smoke 用例与结果并计算 Gate 3.8。
|
|
341
|
+
6. 文本与 JSON 输出必须暴露沙箱模式、是否隔离、失败原因和修复建议。
|
|
342
|
+
|
|
343
|
+
工作区写入保护:
|
|
344
|
+
- 当 `sandbox_deny_workspace_write=true` 时,沙箱执行器必须阻止预跑命令或 smoke 命令写入仓库根目录中的非白名单路径。
|
|
345
|
+
- 白名单仅包含配置声明的结果文件、报告文件和 CLI 显式生成的门禁标记。
|
|
346
|
+
- `always` 模式下,一旦检测到非白名单写入,命令必须失败。
|
|
347
|
+
- `auto` 模式下,无法启用写入保护时必须输出告警,并在 JSON `sandbox.status` 中标记为 `warn`。
|
|
348
|
+
|
|
349
|
+
兼容性:
|
|
350
|
+
- 未配置 `sandbox_mode` 的历史项目等价于 `off`,不得破坏既有 verify / smoke 行为。
|
|
351
|
+
- `init`、`adopt`、`sync` 可以为新项目补齐推荐配置,但不得覆盖用户已有沙箱配置。
|
|
352
|
+
|
|
297
353
|
**验收流程**:
|
|
298
354
|
|
|
299
355
|
1. AI 在 Step 5 生成测试代码时,内嵌 OpenLogos reporter(见 [test-results.md](./test-results.md))
|