open-tui-orchestrator 0.9.6
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/CHANGELOG.md +205 -0
- package/INSTALL-zh.md +96 -0
- package/INSTALL.md +96 -0
- package/LICENSE +48 -0
- package/README-zh.md +181 -0
- package/README.md +181 -0
- package/cli.mjs +37 -0
- package/docs/adapt.md +103 -0
- package/docs/assets/kimicode-agent-swarm-10-subagents.png +0 -0
- package/docs/auto-recovery.md +23 -0
- package/docs/caller-driven.md +121 -0
- package/docs/claude-adapter.md +25 -0
- package/docs/execution-contract.md +70 -0
- package/docs/inactive-windows.md +11 -0
- package/docs/kimi-adapter.md +27 -0
- package/docs/kimi-integration.md +56 -0
- package/docs/maintenance-lock.md +32 -0
- package/docs/openclaw-adapter.md +59 -0
- package/docs/openclaw-assessment-2026-09-06.md +59 -0
- package/docs/opencode-adapter.md +25 -0
- package/docs/pi-adapter.md +58 -0
- package/docs/public-readiness.md +63 -0
- package/docs/release-policy.md +39 -0
- package/docs/security-audit-2026-09-09.md +41 -0
- package/docs/trust-and-safety.md +64 -0
- package/docs/verification-2026-09-06.md +22 -0
- package/docs/verification-recovery-2026-09-06.md +36 -0
- package/orch.mjs +20 -0
- package/package.json +36 -0
- package/release.json +116 -0
- package/repair.mjs +228 -0
- package/scripts/adapt.mjs +35 -0
- package/scripts/agent-auth-prompt.txt +10 -0
- package/scripts/agent.mjs +1 -0
- package/scripts/core/adapt-lib.mjs +219 -0
- package/scripts/core/agent-auth-prompt.txt +10 -0
- package/scripts/core/agent-profiles/hermes.json +59 -0
- package/scripts/core/agent.mjs +1 -0
- package/scripts/core/checkpoint.mjs +38 -0
- package/scripts/core/claude-host.mjs +50 -0
- package/scripts/core/claude-runtime.mjs +111 -0
- package/scripts/core/contracts.mjs +161 -0
- package/scripts/core/host-cli.mjs +204 -0
- package/scripts/core/host-model.mjs +323 -0
- package/scripts/core/host-probe.mjs +16 -0
- package/scripts/core/inactive-window.mjs +32 -0
- package/scripts/core/inactive-window.ps1 +36 -0
- package/scripts/core/kimi-host.mjs +41 -0
- package/scripts/core/kimi-runtime.mjs +140 -0
- package/scripts/core/lease-lock.ps1 +32 -0
- package/scripts/core/leases.mjs +176 -0
- package/scripts/core/maintenance-lock.mjs +77 -0
- package/scripts/core/native-argv.mjs +9 -0
- package/scripts/core/network-policy.mjs +18 -0
- package/scripts/core/openclaw-bootstrap.mjs +25 -0
- package/scripts/core/openclaw-config.mjs +35 -0
- package/scripts/core/openclaw-host.mjs +29 -0
- package/scripts/core/openclaw-runtime.mjs +33 -0
- package/scripts/core/openclaw-window.mjs +44 -0
- package/scripts/core/opencode-host.mjs +80 -0
- package/scripts/core/opencode-runtime.mjs +131 -0
- package/scripts/core/orchestrate-sdk.mjs +2595 -0
- package/scripts/core/pi-host.mjs +29 -0
- package/scripts/core/pi-runtime.mjs +54 -0
- package/scripts/core/pi-shutdown.mjs +16 -0
- package/scripts/core/poll-windows.mjs +48 -0
- package/scripts/core/print-profile.mjs +79 -0
- package/scripts/core/print-runtime.mjs +106 -0
- package/scripts/core/pty-host.mjs +38 -0
- package/scripts/core/recovery.mjs +75 -0
- package/scripts/core/run-board.mjs +155 -0
- package/scripts/core/run-guardian.mjs +130 -0
- package/scripts/core/runner.mjs +274 -0
- package/scripts/core/runtime-context.mjs +23 -0
- package/scripts/core/unit-carrier.mjs +55 -0
- package/scripts/core/unit-command.mjs +96 -0
- package/scripts/core/unit-runtime.mjs +107 -0
- package/scripts/gate.mjs +162 -0
- package/scripts/host-cli.mjs +2 -0
- package/scripts/install-deps.mjs +58 -0
- package/scripts/maintenance-lock.mjs +46 -0
- package/scripts/network-policy.mjs +2 -0
- package/scripts/open-tui-orchestrator-force.mjs +239 -0
- package/scripts/open-tui-orchestrator-preflight.mjs +85 -0
- package/scripts/orchestrate-sdk.mjs +59 -0
- package/scripts/package-lock.json +242 -0
- package/scripts/package.json +9 -0
- package/scripts/platform-guard.mjs +23 -0
- package/scripts/poll-windows.mjs +8 -0
- package/scripts/release-integrity.mjs +94 -0
- package/scripts/runtime-context.mjs +2 -0
- package/scripts/sdk-dependency-check.mjs +32 -0
- package/scripts/todo-list.mjs +89 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* open-tui-orchestrator-preflight — read-only gate that decides whether a requested kind may be executed now.
|
|
4
|
+
*
|
|
5
|
+
* node open-tui-orchestrator-preflight.mjs <write|build|install|test|restart|run|all>
|
|
6
|
+
*
|
|
7
|
+
* Reuses open-tui-orchestrator-force.mjs's armed()/liveWindows() (the same enforcement state the guard uses):
|
|
8
|
+
* - maintenance lock held -> prints the maintenance one-liner, exits 0
|
|
9
|
+
* (本轮跳过 orchestrator,由发起对话直接执行;任何 kind 都放行)。
|
|
10
|
+
* - orchestrator not armed and
|
|
11
|
+
* kind is read-only -> prints ok, exits 0.
|
|
12
|
+
* - orchestrator not armed and
|
|
13
|
+
* kind is real-execution -> prints REFUSED + route hint, exits 1.
|
|
14
|
+
* - current context IS a sanctioned
|
|
15
|
+
* execution window -> prints ok, exits 0.
|
|
16
|
+
* - real-execution kind but NOT a
|
|
17
|
+
* sanctioned window -> prints REFUSED + route hint, exits 1.
|
|
18
|
+
* - read-only kind -> prints ok, exits 0.
|
|
19
|
+
*
|
|
20
|
+
* This only makes a before-you-run decision; it does NOT touch the orchestrator's
|
|
21
|
+
* block/dependency/result-recycling logic.
|
|
22
|
+
*/
|
|
23
|
+
import { armed, liveWindows } from "./open-tui-orchestrator-force.mjs";
|
|
24
|
+
import { lockedForMaintenance, MAINTENANCE_MESSAGE } from "./core/maintenance-lock.mjs";
|
|
25
|
+
|
|
26
|
+
const kind = String(process.argv[2] || "").toLowerCase();
|
|
27
|
+
const realKinds = new Set(["exec", "write", "build", "install", "test", "restart", "start", "stop", "real", "all", "run"]);
|
|
28
|
+
const readKinds = new Set(["read", "analyze", "plan", "view", "query", "search", "status", "list", "preview", "ls", "show", "check", "validate", "dry-run", "help"]);
|
|
29
|
+
|
|
30
|
+
// 维护锁最优先:上锁期间任何调用只回固定话术并放行(exit 0),本轮直接执行。
|
|
31
|
+
if (lockedForMaintenance()) {
|
|
32
|
+
console.log(MAINTENANCE_MESSAGE);
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 统一校验:空/未知 kind 一律报错退出 1(不因窗口状态放宽),决策输出统一走 stdout 供下游捕获。
|
|
37
|
+
if (!kind) {
|
|
38
|
+
console.log("[preflight] error: missing kind argument (real: " + [...realKinds].join("/") + " | read-only: " + [...readKinds].join("/") + ").");
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
if (!realKinds.has(kind) && !readKinds.has(kind)) {
|
|
42
|
+
console.log("[preflight] invalid/unknown kind: '" + kind + "' (allowed real: " + [...realKinds].join("/") + " | read-only: " + [...readKinds].join("/") + ").");
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!armed()) {
|
|
47
|
+
if (realKinds.has(kind)) {
|
|
48
|
+
console.log(
|
|
49
|
+
"REFUSED: preflight '" + kind + "' is a real-execution kind but the orchestrator is not armed (enforcement disabled). " +
|
|
50
|
+
"Route through node orchestrate-sdk.mjs --run-windows \"<request>\" (a real, visible agent TUI window)."
|
|
51
|
+
);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
console.log("ok: read-only kind (" + kind + ").");
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// liveWindows() 为跨进程快照(存在竞态),此处是 before-you-run 的近似判定;如需严格应在执行前紧邻复验。
|
|
59
|
+
const envWindow = process.env.ORCH_WINDOW === "1";
|
|
60
|
+
const alive = liveWindows().length;
|
|
61
|
+
// 与 force --assert 保持一致:ORCH_WINDOW=1 但无活窗 → env 单独不可采信,一律 REFUSED(含只读 kind)。
|
|
62
|
+
if (envWindow && alive === 0) {
|
|
63
|
+
console.log(
|
|
64
|
+
"REFUSED: ORCH_WINDOW=1 but no live registered orchestration window (env var alone is not accepted; may be forged). " +
|
|
65
|
+
"Route through node orchestrate-sdk.mjs --run-windows \"<request>\" (a real, visible agent TUI window)."
|
|
66
|
+
);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
// Only a process that (a) sets ORCH_WINDOW=1 AND (b) has a live registered window is sanctioned.
|
|
70
|
+
// A headless/script context running while a legitimate window happens to be open must NOT pass.
|
|
71
|
+
if (envWindow && alive > 0) {
|
|
72
|
+
console.log("ok: sanctioned orchestration window; '" + kind + "' may proceed.");
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (realKinds.has(kind)) {
|
|
77
|
+
console.log(
|
|
78
|
+
"REFUSED: preflight '" + kind + "' is a real-execution kind but this is NOT a sanctioned orchestration window. " +
|
|
79
|
+
"Route through node orchestrate-sdk.mjs --run-windows \"<request>\" (a real, visible agent TUI window)."
|
|
80
|
+
);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
console.log("ok: read-only kind (" + kind + ").");
|
|
85
|
+
process.exit(0);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {fileURLToPath} from 'node:url';
|
|
4
|
+
import {verifyRelease} from './release-integrity.mjs';
|
|
5
|
+
import {enforceWindowsOnly} from './platform-guard.mjs';
|
|
6
|
+
|
|
7
|
+
// 机器可调用契约:**作为主入口时,任何失败只回一行** `[orchestrator] FATAL: <原因>`
|
|
8
|
+
// (完整栈帧仅 ORCH_DEBUG=1 时才打)——调用方的管道里永远不该出现裸 Node 栈帧。
|
|
9
|
+
// 作为模块被 import 时保持原语义:封印/自适配失败照旧抛出(fail-closed 不受影响),
|
|
10
|
+
// 只是不再把栈帧甩到调用者脸上。
|
|
11
|
+
const IS_MAIN = !!process.argv[1] && path.resolve(process.argv[1]).toLowerCase() === fileURLToPath(import.meta.url).toLowerCase();
|
|
12
|
+
function fatal(error) {
|
|
13
|
+
console.error('[orchestrator] FATAL: ' + (error && error.message ? error.message : String(error)));
|
|
14
|
+
if (process.env.ORCH_DEBUG === '1' && error && error.stack) console.error(error.stack);
|
|
15
|
+
}
|
|
16
|
+
// 引导入口(--install-deps)必须先于一切依赖存在:本包自己的 SDK 依赖(node-pty / codex-sdk)正是
|
|
17
|
+
// 它要装的东西,而引擎在 ORCH_AGENT=codex 时会顶层 import SDK——先要求「依赖已装/引擎可加载」
|
|
18
|
+
// 会让缺依赖的用户永远够不着这个入口(2026-09-14 真机回归抓到的鸡生蛋问题)。
|
|
19
|
+
const bootstrapInstallDeps = IS_MAIN && process.argv.slice(2).includes('--install-deps');
|
|
20
|
+
// Windows-only 是首页承诺,就要在入口强制(共享实现见 scripts/platform-guard.mjs;cli.mjs / orch.mjs
|
|
21
|
+
// 也在入口调用同一份)。这里覆盖直接跑 scripts/orchestrate-sdk.mjs 的调用方。
|
|
22
|
+
if (IS_MAIN && enforceWindowsOnly(process.argv.slice(2))) process.exit(1);
|
|
23
|
+
try {
|
|
24
|
+
if (!bootstrapInstallDeps) {
|
|
25
|
+
verifyRelease();
|
|
26
|
+
// 自动适配钩子(显式开关):检测到未适配的 agent 且仓库未上锁时,先自动完成适配
|
|
27
|
+
// (探测→画像→活验→重封→提交)再继续本轮调用;适配失败则 fail-closed 报错退出。
|
|
28
|
+
// 注意:adapt 会写入 scripts/core/agent-profiles/<id>.json 并重封提交——这是显式要求
|
|
29
|
+
// 的自修改通道,只有 ORCH_AUTO_ADAPT=1 时才启用。
|
|
30
|
+
if (process.env.ORCH_AUTO_ADAPT === '1') {
|
|
31
|
+
const { autoAdaptIfNeeded } = await import('./core/adapt-lib.mjs');
|
|
32
|
+
await autoAdaptIfNeeded();
|
|
33
|
+
}
|
|
34
|
+
const requestedAgent = String(process.env.ORCH_AGENT || '').trim().toLowerCase();
|
|
35
|
+
if (requestedAgent === 'codex' || (!requestedAgent && (process.env.CODEX_SESSION_ID || process.env.CODEX_THREAD_ID))) await import('./sdk-dependency-check.mjs');
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if (IS_MAIN) { fatal(error); process.exit(1); }
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
// 引导路径不进引擎:缺依赖时引擎根本加载不起来。装完依赖后,再走正常入口。
|
|
42
|
+
const engine = bootstrapInstallDeps ? null : await import('./core/orchestrate-sdk.mjs');
|
|
43
|
+
export const {
|
|
44
|
+
main, reportFatal, buildPsArrayLit, capPlanBlocks, buildWindowWaves, applyAuthPromptPolicy, readTail,
|
|
45
|
+
classifyBlockComplexity, classifyExecutionMode, appendModelEffortArgs, genericProjectSplit,
|
|
46
|
+
trySingleBlock, conflictByKeys, loadCachedDecompose, buildBlocksFromTasks, resolveHints,
|
|
47
|
+
extractTasks, classifyPrompt, buildBlockPrompt, levelWaves, decomposeHash, checkDiskSpace,
|
|
48
|
+
readPidFile, pidAliveCheck, closeWindowByPid, windowGenuinelyPopped, extractResult, extractUsage,
|
|
49
|
+
readReportFromResult, sessionDir, writeInteractiveLauncher, staleTransientFiles,
|
|
50
|
+
pidFileOfTransient, tryReserveWindow, commitReservation, cancelReservation, computeMaxWindows,
|
|
51
|
+
blockKeys, keysOverlap, scanTaskComplete, windowExecutionPolicy, __setOrchHooks, isOrchestratorSelfMod, resolveCodexHome,
|
|
52
|
+
withRegistryLock, REGISTRY_LOCK_FILE,
|
|
53
|
+
} = engine ?? {};
|
|
54
|
+
if (bootstrapInstallDeps) {
|
|
55
|
+
const { installDepsCli } = await import('./install-deps.mjs');
|
|
56
|
+
process.exitCode = installDepsCli();
|
|
57
|
+
} else if (IS_MAIN) {
|
|
58
|
+
engine.main().catch((e) => { fatal(e); process.exitCode = 1; });
|
|
59
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "open-tui-orchestrator",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "open-tui-orchestrator",
|
|
9
|
+
"version": "0.2.0",
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@lydell/node-pty": "1.2.0-beta.15",
|
|
12
|
+
"@openai/codex-sdk": "0.149.0"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"node_modules/@lydell/node-pty": {
|
|
16
|
+
"version": "1.2.0-beta.15",
|
|
17
|
+
"resolved": "https://registry.npmjs.org/@lydell/node-pty/-/node-pty-1.2.0-beta.15.tgz",
|
|
18
|
+
"integrity": "sha512-Br8wBxzbxFwdWgk9uQ+rdzE0xfoxOK4QuGH54swhRwc5IxP6H9Y1/bcyazRGvNUs6XkB5qNVkezuKSRxUwZe7A==",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"optionalDependencies": {
|
|
21
|
+
"@lydell/node-pty-darwin-arm64": "1.2.0-beta.15",
|
|
22
|
+
"@lydell/node-pty-darwin-x64": "1.2.0-beta.15",
|
|
23
|
+
"@lydell/node-pty-linux-arm64": "1.2.0-beta.15",
|
|
24
|
+
"@lydell/node-pty-linux-x64": "1.2.0-beta.15",
|
|
25
|
+
"@lydell/node-pty-win32-arm64": "1.2.0-beta.15",
|
|
26
|
+
"@lydell/node-pty-win32-x64": "1.2.0-beta.15"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"node_modules/@lydell/node-pty-darwin-arm64": {
|
|
30
|
+
"version": "1.2.0-beta.15",
|
|
31
|
+
"resolved": "https://registry.npmjs.org/@lydell/node-pty-darwin-arm64/-/node-pty-darwin-arm64-1.2.0-beta.15.tgz",
|
|
32
|
+
"integrity": "sha512-6TSBbzdcLiNTHl1mTuzflqXrkmcC36USVGvERoDgvHk2ItEDaMaFZuAJ1CqPmwYj0DyhCS16TVS8OGK9xZnjyQ==",
|
|
33
|
+
"cpu": [
|
|
34
|
+
"arm64"
|
|
35
|
+
],
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"optional": true,
|
|
38
|
+
"os": [
|
|
39
|
+
"darwin"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"node_modules/@lydell/node-pty-darwin-x64": {
|
|
43
|
+
"version": "1.2.0-beta.15",
|
|
44
|
+
"resolved": "https://registry.npmjs.org/@lydell/node-pty-darwin-x64/-/node-pty-darwin-x64-1.2.0-beta.15.tgz",
|
|
45
|
+
"integrity": "sha512-yDT2oqPqYMBScyuk1U9Rg5VKcrbMOD9o9jWYYamDADA3NSbUISroPChrqYRQ74Y7BQtNH4gqYAiWOZRi5uQZ0Q==",
|
|
46
|
+
"cpu": [
|
|
47
|
+
"x64"
|
|
48
|
+
],
|
|
49
|
+
"license": "MIT",
|
|
50
|
+
"optional": true,
|
|
51
|
+
"os": [
|
|
52
|
+
"darwin"
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"node_modules/@lydell/node-pty-linux-arm64": {
|
|
56
|
+
"version": "1.2.0-beta.15",
|
|
57
|
+
"resolved": "https://registry.npmjs.org/@lydell/node-pty-linux-arm64/-/node-pty-linux-arm64-1.2.0-beta.15.tgz",
|
|
58
|
+
"integrity": "sha512-wkbNF7dYAmtJv+o2+iztVlNwnUB4B0uX0wh/UD+mwMcmE2gNMnW9GChXO7fEE5XJokD0vB5idiHpGegaN+G/sg==",
|
|
59
|
+
"cpu": [
|
|
60
|
+
"arm64"
|
|
61
|
+
],
|
|
62
|
+
"license": "MIT",
|
|
63
|
+
"optional": true,
|
|
64
|
+
"os": [
|
|
65
|
+
"linux"
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"node_modules/@lydell/node-pty-linux-x64": {
|
|
69
|
+
"version": "1.2.0-beta.15",
|
|
70
|
+
"resolved": "https://registry.npmjs.org/@lydell/node-pty-linux-x64/-/node-pty-linux-x64-1.2.0-beta.15.tgz",
|
|
71
|
+
"integrity": "sha512-+U/5AVvHT6W+8OCYcnJgN0Qgc0ycO3TfD6aaFJHK+WHij797f8gsi5dV1HEO9l6YQmWCD+VL5gaLDhx3mxHwCA==",
|
|
72
|
+
"cpu": [
|
|
73
|
+
"x64"
|
|
74
|
+
],
|
|
75
|
+
"license": "MIT",
|
|
76
|
+
"optional": true,
|
|
77
|
+
"os": [
|
|
78
|
+
"linux"
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
"node_modules/@lydell/node-pty-win32-arm64": {
|
|
82
|
+
"version": "1.2.0-beta.15",
|
|
83
|
+
"resolved": "https://registry.npmjs.org/@lydell/node-pty-win32-arm64/-/node-pty-win32-arm64-1.2.0-beta.15.tgz",
|
|
84
|
+
"integrity": "sha512-pyAk91w7wnnKrD4mrHXtIXRfmzSWV5bEzvRhurXcMCtCc2TJ424ciUskIgWMhAPP6y3KyUnqElj+U6kY3iOt0A==",
|
|
85
|
+
"cpu": [
|
|
86
|
+
"arm64"
|
|
87
|
+
],
|
|
88
|
+
"license": "MIT",
|
|
89
|
+
"optional": true,
|
|
90
|
+
"os": [
|
|
91
|
+
"win32"
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
"node_modules/@lydell/node-pty-win32-x64": {
|
|
95
|
+
"version": "1.2.0-beta.15",
|
|
96
|
+
"resolved": "https://registry.npmjs.org/@lydell/node-pty-win32-x64/-/node-pty-win32-x64-1.2.0-beta.15.tgz",
|
|
97
|
+
"integrity": "sha512-2f8twEmDVxZ7drchAXjtevpmSPhFok0avAnzXro4t5gmz0xsPNKkoZvymwtuIS3xo7PzQqZOPQ/YzwEMb7oIzQ==",
|
|
98
|
+
"cpu": [
|
|
99
|
+
"x64"
|
|
100
|
+
],
|
|
101
|
+
"license": "MIT",
|
|
102
|
+
"optional": true,
|
|
103
|
+
"os": [
|
|
104
|
+
"win32"
|
|
105
|
+
]
|
|
106
|
+
},
|
|
107
|
+
"node_modules/@openai/codex": {
|
|
108
|
+
"version": "0.149.0",
|
|
109
|
+
"resolved": "https://registry.npmmirror.com/@openai/codex/-/codex-0.149.0.tgz",
|
|
110
|
+
"integrity": "sha512-i4dryj2Y1j+00Mb5n+0n71EYnTK9/KDc2cdFo/dXD0d1oTog2bhUssKDEIOnKmnEf51P0Z/HJTWvTKw/UHyOvQ==",
|
|
111
|
+
"license": "Apache-2.0",
|
|
112
|
+
"bin": {
|
|
113
|
+
"codex": "bin/codex.js"
|
|
114
|
+
},
|
|
115
|
+
"engines": {
|
|
116
|
+
"node": ">=16"
|
|
117
|
+
},
|
|
118
|
+
"optionalDependencies": {
|
|
119
|
+
"@openai/codex-darwin-arm64": "npm:@openai/codex@0.149.0-darwin-arm64",
|
|
120
|
+
"@openai/codex-darwin-x64": "npm:@openai/codex@0.149.0-darwin-x64",
|
|
121
|
+
"@openai/codex-linux-arm64": "npm:@openai/codex@0.149.0-linux-arm64",
|
|
122
|
+
"@openai/codex-linux-x64": "npm:@openai/codex@0.149.0-linux-x64",
|
|
123
|
+
"@openai/codex-win32-arm64": "npm:@openai/codex@0.149.0-win32-arm64",
|
|
124
|
+
"@openai/codex-win32-x64": "npm:@openai/codex@0.149.0-win32-x64"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"node_modules/@openai/codex-darwin-arm64": {
|
|
128
|
+
"name": "@openai/codex",
|
|
129
|
+
"version": "0.149.0-darwin-arm64",
|
|
130
|
+
"resolved": "https://registry.npmmirror.com/@openai/codex/-/codex-0.149.0-darwin-arm64.tgz",
|
|
131
|
+
"integrity": "sha512-GsZJbzBWiD48RETrO8VHGAQNgfSrUVxItXZFeD87wswatPi0+lKuQo8Dx4nMYmOZhZrVtwr3al/feRrZxnDV8Q==",
|
|
132
|
+
"cpu": [
|
|
133
|
+
"arm64"
|
|
134
|
+
],
|
|
135
|
+
"license": "Apache-2.0",
|
|
136
|
+
"optional": true,
|
|
137
|
+
"os": [
|
|
138
|
+
"darwin"
|
|
139
|
+
],
|
|
140
|
+
"engines": {
|
|
141
|
+
"node": ">=16"
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
"node_modules/@openai/codex-darwin-x64": {
|
|
145
|
+
"name": "@openai/codex",
|
|
146
|
+
"version": "0.149.0-darwin-x64",
|
|
147
|
+
"resolved": "https://registry.npmmirror.com/@openai/codex/-/codex-0.149.0-darwin-x64.tgz",
|
|
148
|
+
"integrity": "sha512-H+mMgW3Nhc5QzGWEklCoFqACuOc0cVpgPkPQRw0LShoK7P5664T6BRnyl1yzT6orKPKv49cXry7DIWWZ19SanQ==",
|
|
149
|
+
"cpu": [
|
|
150
|
+
"x64"
|
|
151
|
+
],
|
|
152
|
+
"license": "Apache-2.0",
|
|
153
|
+
"optional": true,
|
|
154
|
+
"os": [
|
|
155
|
+
"darwin"
|
|
156
|
+
],
|
|
157
|
+
"engines": {
|
|
158
|
+
"node": ">=16"
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
"node_modules/@openai/codex-linux-arm64": {
|
|
162
|
+
"name": "@openai/codex",
|
|
163
|
+
"version": "0.149.0-linux-arm64",
|
|
164
|
+
"resolved": "https://registry.npmmirror.com/@openai/codex/-/codex-0.149.0-linux-arm64.tgz",
|
|
165
|
+
"integrity": "sha512-fAXPpvIob+11RNZJS9CVVTsKb+V4Hw3woGFPj42D7fU2wBJUKI2jfAc4fLJNtrpwRecLeW601mtkMHOSIbWuuA==",
|
|
166
|
+
"cpu": [
|
|
167
|
+
"arm64"
|
|
168
|
+
],
|
|
169
|
+
"license": "Apache-2.0",
|
|
170
|
+
"optional": true,
|
|
171
|
+
"os": [
|
|
172
|
+
"linux"
|
|
173
|
+
],
|
|
174
|
+
"engines": {
|
|
175
|
+
"node": ">=16"
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
"node_modules/@openai/codex-linux-x64": {
|
|
179
|
+
"name": "@openai/codex",
|
|
180
|
+
"version": "0.149.0-linux-x64",
|
|
181
|
+
"resolved": "https://registry.npmmirror.com/@openai/codex/-/codex-0.149.0-linux-x64.tgz",
|
|
182
|
+
"integrity": "sha512-uZXaN9JPxu0/jjnqqJeTd4kRYPnjVZK3MiVndfG1mHhEaoDKL7ScWHfPqvAEOjwsSDEmQSlMfUkmvYp/CHciYw==",
|
|
183
|
+
"cpu": [
|
|
184
|
+
"x64"
|
|
185
|
+
],
|
|
186
|
+
"license": "Apache-2.0",
|
|
187
|
+
"optional": true,
|
|
188
|
+
"os": [
|
|
189
|
+
"linux"
|
|
190
|
+
],
|
|
191
|
+
"engines": {
|
|
192
|
+
"node": ">=16"
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"node_modules/@openai/codex-sdk": {
|
|
196
|
+
"version": "0.149.0",
|
|
197
|
+
"resolved": "https://registry.npmmirror.com/@openai/codex-sdk/-/codex-sdk-0.149.0.tgz",
|
|
198
|
+
"integrity": "sha512-wQCKyRR8JScUv+48wIOot8Oapt/0wsud1WptPTCamXYXZXjtpXVZ1UwACipbpnnesB1BLEeOiN1IFw2fgNN2Ww==",
|
|
199
|
+
"license": "Apache-2.0",
|
|
200
|
+
"dependencies": {
|
|
201
|
+
"@openai/codex": "0.149.0"
|
|
202
|
+
},
|
|
203
|
+
"engines": {
|
|
204
|
+
"node": ">=18"
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
"node_modules/@openai/codex-win32-arm64": {
|
|
208
|
+
"name": "@openai/codex",
|
|
209
|
+
"version": "0.149.0-win32-arm64",
|
|
210
|
+
"resolved": "https://registry.npmmirror.com/@openai/codex/-/codex-0.149.0-win32-arm64.tgz",
|
|
211
|
+
"integrity": "sha512-pUd8MzuwtqT5DhM1NUE1gETWIZ9fkDA1XB7tt9YNIi/peUgLuziQgZd7o0bNON4cNzgbil1YUN1qDTgQm0g3pg==",
|
|
212
|
+
"cpu": [
|
|
213
|
+
"arm64"
|
|
214
|
+
],
|
|
215
|
+
"license": "Apache-2.0",
|
|
216
|
+
"optional": true,
|
|
217
|
+
"os": [
|
|
218
|
+
"win32"
|
|
219
|
+
],
|
|
220
|
+
"engines": {
|
|
221
|
+
"node": ">=16"
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
"node_modules/@openai/codex-win32-x64": {
|
|
225
|
+
"name": "@openai/codex",
|
|
226
|
+
"version": "0.149.0-win32-x64",
|
|
227
|
+
"resolved": "https://registry.npmmirror.com/@openai/codex/-/codex-0.149.0-win32-x64.tgz",
|
|
228
|
+
"integrity": "sha512-qKbwSOOO/fdhQ5MlXE2fts6taPxRPZ/zqeC+eqHD72hLRymV9rFCUbUxOCquognUPRPvS/2/kRCV0UVhoDd3yQ==",
|
|
229
|
+
"cpu": [
|
|
230
|
+
"x64"
|
|
231
|
+
],
|
|
232
|
+
"license": "Apache-2.0",
|
|
233
|
+
"optional": true,
|
|
234
|
+
"os": [
|
|
235
|
+
"win32"
|
|
236
|
+
],
|
|
237
|
+
"engines": {
|
|
238
|
+
"node": ">=16"
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* platform-guard.mjs — enforces the front-page promise "Windows-only today".
|
|
4
|
+
*
|
|
5
|
+
* The promise is worth nothing if it is only a sentence in the README, and it is *worse* than
|
|
6
|
+
* nothing if a non-Windows user gets a half-working install: measured 2026-09-15, an entry with
|
|
7
|
+
* no guard reported a healthy `--doctor` (exit 0, `terminal: "wt"`) on a simulated Linux platform.
|
|
8
|
+
*
|
|
9
|
+
* So the refusal happens **at the entry** (cli.mjs / orch.mjs / the SDK wrapper), before host
|
|
10
|
+
* resolution, before any model call, before any state. Three entries stay available because they
|
|
11
|
+
* are platform-neutral and are exactly what somebody debugging a platform issue needs:
|
|
12
|
+
* `--version`, `--help`, `--install-deps`.
|
|
13
|
+
*
|
|
14
|
+
* Callers: `if (enforceWindowsOnly(args)) process.exit(1);`
|
|
15
|
+
*/
|
|
16
|
+
export const PLATFORM_NEUTRAL_ARGS = new Set(['--version', '--help', '-h', '--install-deps']);
|
|
17
|
+
|
|
18
|
+
export function enforceWindowsOnly(argv = process.argv.slice(2), platform = process.platform) {
|
|
19
|
+
if (platform === 'win32') return false;
|
|
20
|
+
if (argv.some((arg) => PLATFORM_NEUTRAL_ARGS.has(arg))) return false;
|
|
21
|
+
console.error('[orchestrator] FATAL: Windows-only: this build drives Windows Terminal, the ConPTY carrier and the HKCU coordinator; detected platform "' + platform + '" — only --version, --help and --install-deps are available here');
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {verifyRelease} from './release-integrity.mjs';
|
|
3
|
+
verifyRelease();
|
|
4
|
+
const engine = await import('./core/poll-windows.mjs');
|
|
5
|
+
export const {TERMINAL, SUCCESS, summaryComplete, summaryTerminal, failures, loadSummary, parsePollingOptions, main} = engine;
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {fileURLToPath} from 'node:url';
|
|
8
|
+
if(process.argv[1]&&path.resolve(process.argv[1]).toLowerCase()===fileURLToPath(import.meta.url).toLowerCase())engine.main().catch(e=>{console.error(e.stack||e.message);process.exitCode=1;});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import crypto from 'node:crypto';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
|
|
6
|
+
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
7
|
+
const EXCLUDED_DIRS = new Set(['.git', '.plugin-eval', 'coverage', 'node_modules', 'temp']);
|
|
8
|
+
const EXCLUDED_FILES = new Set(['.agent-cache.json']);
|
|
9
|
+
|
|
10
|
+
function sha256(file) {
|
|
11
|
+
return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function walkDirectory(dir, base, out) {
|
|
15
|
+
const stat = fs.lstatSync(dir);
|
|
16
|
+
if (stat.isSymbolicLink()) throw new Error('RELEASE_LINK_FORBIDDEN: ' + dir);
|
|
17
|
+
if (!stat.isDirectory()) throw new Error('RELEASE_DIRECTORY_MISSING: ' + dir);
|
|
18
|
+
for (const entry of fs.readdirSync(dir, {withFileTypes: true}).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
19
|
+
if (entry.isDirectory() && EXCLUDED_DIRS.has(entry.name)) continue;
|
|
20
|
+
if (entry.isFile() && EXCLUDED_FILES.has(entry.name)) continue;
|
|
21
|
+
const file = path.join(dir, entry.name);
|
|
22
|
+
const rel = path.join(base, entry.name).replaceAll('\\', '/');
|
|
23
|
+
if (entry.isSymbolicLink()) throw new Error('RELEASE_LINK_FORBIDDEN: ' + file);
|
|
24
|
+
if (entry.isDirectory()) walkDirectory(file, rel, out);
|
|
25
|
+
else if (entry.isFile()) out[rel] = sha256(file);
|
|
26
|
+
else throw new Error('RELEASE_ENTRY_UNSUPPORTED: ' + file);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function walkFile(file, rel, out) {
|
|
31
|
+
const stat = fs.lstatSync(file);
|
|
32
|
+
if (stat.isSymbolicLink()) throw new Error('RELEASE_LINK_FORBIDDEN: ' + file);
|
|
33
|
+
if (!stat.isFile()) throw new Error('RELEASE_FILE_MISSING: ' + file);
|
|
34
|
+
out[rel.replaceAll('\\', '/')] = sha256(file);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function coreInventory(packageRoot = root) {
|
|
38
|
+
const core = path.join(packageRoot, 'scripts', 'core');
|
|
39
|
+
const files = {};
|
|
40
|
+
walkDirectory(core, '', files);
|
|
41
|
+
if (!files['orchestrate-sdk.mjs'] || !files['contracts.mjs']) throw new Error('RELEASE_CORE_MISSING');
|
|
42
|
+
return files;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function runtimeInventory(packageRoot = root) {
|
|
46
|
+
const files = {};
|
|
47
|
+
for (const rel of ['cli.mjs', 'package.json', 'repair.mjs']) {
|
|
48
|
+
const file = path.join(packageRoot, rel);
|
|
49
|
+
if (fs.existsSync(file)) walkFile(file, rel, files);
|
|
50
|
+
}
|
|
51
|
+
const scripts = path.join(packageRoot, 'scripts');
|
|
52
|
+
if (fs.existsSync(scripts)) walkDirectory(scripts, 'scripts', files);
|
|
53
|
+
return files;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function currentCoreVersion(packageRoot) {
|
|
57
|
+
const text = fs.readFileSync(path.join(packageRoot, 'scripts', 'core', 'contracts.mjs'), 'utf8');
|
|
58
|
+
return /CORE_VERSION\s*=\s*'([^']+)'/.exec(text)?.[1] || null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function verifyRelease(packageRoot = root) {
|
|
62
|
+
const manifestPath = path.join(packageRoot, 'release.json');
|
|
63
|
+
let manifest;
|
|
64
|
+
try {
|
|
65
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
66
|
+
} catch (error) {
|
|
67
|
+
throw new Error('RELEASE_MANIFEST_INVALID: ' + (error?.message || String(error)));
|
|
68
|
+
}
|
|
69
|
+
if (manifest.schema !== 2) throw new Error('RELEASE_SCHEMA_UNSUPPORTED');
|
|
70
|
+
const current = coreInventory(packageRoot);
|
|
71
|
+
if (JSON.stringify(current) !== JSON.stringify(manifest.files)) {
|
|
72
|
+
throw new Error('RELEASE_INTEGRITY_FAILED: core differs from the verified release; reinstall the release');
|
|
73
|
+
}
|
|
74
|
+
const version = currentCoreVersion(packageRoot);
|
|
75
|
+
if (!version || manifest.coreVersion !== version) {
|
|
76
|
+
throw new Error('RELEASE_INTEGRITY_FAILED: core version differs from the verified release');
|
|
77
|
+
}
|
|
78
|
+
const runtime = runtimeInventory(packageRoot);
|
|
79
|
+
if (JSON.stringify(runtime) !== JSON.stringify(manifest.runtimeFiles)) {
|
|
80
|
+
throw new Error('RELEASE_INTEGRITY_FAILED: runtime differs from the verified release; reinstall the release');
|
|
81
|
+
}
|
|
82
|
+
return {schema: manifest.schema, coreVersion: manifest.coreVersion, files: Object.keys(current).length, runtimeFiles: Object.keys(runtime).length};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (process.argv[1] && path.resolve(process.argv[1]).toLowerCase() === fileURLToPath(import.meta.url).toLowerCase()) {
|
|
86
|
+
if (process.argv.includes('--seal')) {
|
|
87
|
+
const files = coreInventory(root);
|
|
88
|
+
const coreVersion = currentCoreVersion(root);
|
|
89
|
+
if (!coreVersion) throw new Error('Missing core version');
|
|
90
|
+
const runtimeFiles = runtimeInventory(root);
|
|
91
|
+
fs.writeFileSync(path.join(root, 'release.json'), JSON.stringify({schema: 2, coreVersion, files, runtimeFiles}, null, 2) + '\n', 'utf8');
|
|
92
|
+
}
|
|
93
|
+
console.log(JSON.stringify(verifyRelease()));
|
|
94
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {fileURLToPath} from 'node:url';
|
|
4
|
+
|
|
5
|
+
const scripts=fileURLToPath(new URL('.',import.meta.url));
|
|
6
|
+
const instruction='npm ci --prefix '+JSON.stringify(scripts);
|
|
7
|
+
let entry;
|
|
8
|
+
try{entry=fileURLToPath(import.meta.resolve('@openai/codex-sdk'));}
|
|
9
|
+
catch(error){
|
|
10
|
+
if(['ERR_INVALID_PACKAGE_CONFIG','ERR_PACKAGE_PATH_NOT_EXPORTED'].includes(error.code))throw new Error('RELEASE_DEPENDENCY_DAMAGED: SDK package metadata is invalid; restore with '+instruction);
|
|
11
|
+
if(error.code!=='ERR_MODULE_NOT_FOUND')throw error;
|
|
12
|
+
throw new Error('RELEASE_DEPENDENCY_MISSING: install the locked SDK dependency with '+instruction);
|
|
13
|
+
}
|
|
14
|
+
try{
|
|
15
|
+
if(!fs.statSync(entry).isFile())throw new Error('not a file');
|
|
16
|
+
fs.accessSync(entry,fs.constants.R_OK);
|
|
17
|
+
}catch{throw new Error('RELEASE_DEPENDENCY_DAMAGED: SDK entry is missing or unreadable; restore with '+instruction);}
|
|
18
|
+
const expected=JSON.parse(fs.readFileSync(path.join(scripts,'package.json'),'utf8')).dependencies['@openai/codex-sdk'];
|
|
19
|
+
let dir=path.dirname(entry),actual;
|
|
20
|
+
while(true){
|
|
21
|
+
const manifest=path.join(dir,'package.json');
|
|
22
|
+
if(fs.existsSync(manifest)){
|
|
23
|
+
const pkg=JSON.parse(fs.readFileSync(manifest,'utf8'));
|
|
24
|
+
if(pkg.name==='@openai/codex-sdk'){actual=pkg.version;break;}
|
|
25
|
+
}
|
|
26
|
+
const parent=path.dirname(dir);if(parent===dir)break;dir=parent;
|
|
27
|
+
}
|
|
28
|
+
if(actual!==expected)throw new Error(`RELEASE_DEPENDENCY_VERSION: expected ${expected}, found ${actual||'unknown'}; restore with ${instruction}`);
|
|
29
|
+
try{
|
|
30
|
+
const sdk=await import('@openai/codex-sdk');
|
|
31
|
+
if(typeof sdk.Codex!=='function'||typeof sdk.Codex.prototype?.startThread!=='function'||typeof sdk.Codex.prototype?.resumeThread!=='function')throw new Error('missing interfaces');
|
|
32
|
+
}catch{throw new Error('RELEASE_DEPENDENCY_DAMAGED: SDK cannot load or lacks required interfaces; restore with '+instruction);}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* todo-list.mjs — TODO LIST 完整性校验器
|
|
4
|
+
*
|
|
5
|
+
* 用途:任何 agent(主对话 / TUI 窗 / 子代理)在做 TODO LIST 后,用它校验清单
|
|
6
|
+
* 是否【有头有尾、覆盖首中尾、每步含动作/产物/验证、有依赖顺序】,防"只规划不落地"。
|
|
7
|
+
*
|
|
8
|
+
* 用法:
|
|
9
|
+
* node todo-list.mjs --check <清单文件> [--json]
|
|
10
|
+
* node todo-list.mjs --check <清单文件> --strict # 更严格:必须有"目标"与"收尾验证/清理"且步数>=1
|
|
11
|
+
*
|
|
12
|
+
* 退出码:校验通过=0;存在缺失=1(供 agent 在未过校验前禁止进入执行)。
|
|
13
|
+
*/
|
|
14
|
+
import fs from 'node:fs';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
|
|
17
|
+
function getArg(name, dflt) {
|
|
18
|
+
const i = process.argv.indexOf(name);
|
|
19
|
+
return (i >= 0 && process.argv[i + 1] != null) ? process.argv[i + 1] : dflt;
|
|
20
|
+
}
|
|
21
|
+
const hasFlag = (n) => process.argv.includes(n);
|
|
22
|
+
const JSON_MODE = hasFlag('--json');
|
|
23
|
+
const STRICT = hasFlag('--strict');
|
|
24
|
+
const file = getArg('--check', null);
|
|
25
|
+
|
|
26
|
+
function reportOk(ok, checks) {
|
|
27
|
+
const summary = { ok, checks };
|
|
28
|
+
if (JSON_MODE) { console.log(JSON.stringify(summary, null, 2)); }
|
|
29
|
+
else {
|
|
30
|
+
console.log(ok ? 'TODO LIST 校验通过(有头有尾、覆盖全链)。' : 'TODO LIST 校验未通过(见下)。');
|
|
31
|
+
for (const c of checks) console.log(' [' + (c.ok ? 'PASS' : 'FAIL') + '] ' + c.name + (c.note ? ' — ' + c.note : ''));
|
|
32
|
+
}
|
|
33
|
+
process.exit(ok ? 0 : 1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function check(text, strict) {
|
|
37
|
+
const t = String(text || '');
|
|
38
|
+
const checks = [];
|
|
39
|
+
const L = (name, ok, note) => checks.push({ name, ok: !!ok, note: note || '' });
|
|
40
|
+
|
|
41
|
+
// 有头:目标/目的/背景 + 前置/环境盘点
|
|
42
|
+
const hasGoal = /目标|目的|goal|objective|任务目标|要完成|本次任务/i.test(t);
|
|
43
|
+
L('有头-目标明确', hasGoal, hasGoal ? '' : '清单缺少“目标/目的”说明(有头)');
|
|
44
|
+
const hasPre = /前置|现状|环境盘点|环境|背景|依赖条件|先确认|先区分|已确认|已知/i.test(t);
|
|
45
|
+
L('有头-前置/环境盘点', hasPre, hasPre ? '' : '缺少“前置条件/环境盘点/现状”');
|
|
46
|
+
|
|
47
|
+
// 中间:动作/产物/验证 + 依赖顺序
|
|
48
|
+
const stepLines = t.split(/\n/).map((l) => l.trim()).filter((l) => /^\s*\d+[)\.、:]/.test(l) || /^\s*[-*•] /.test(l));
|
|
49
|
+
L('中间-步骤数>0', stepLines.length > 0, '步骤数=' + stepLines.length);
|
|
50
|
+
const hasAction = /动作|执行|创建|写入|运行|改|建|task|步骤|①|②|③/ .test(t);
|
|
51
|
+
const hasProduct = /产物|输出|落地|文件|配置|结果|生成|produce|output/i.test(t);
|
|
52
|
+
const hasVerify = /验证|自验|读回|回归|核验|校验|check|verify|确认/i.test(t);
|
|
53
|
+
L('中间-含动作', hasAction, hasAction ? '' : '缺少“动作”要素');
|
|
54
|
+
L('中间-含产物', hasProduct, hasProduct ? '' : '缺少“产物”要素(落地物)');
|
|
55
|
+
L('中间-含验证', hasVerify, hasVerify ? '' : '缺少“验证点”(读回/回归)');
|
|
56
|
+
const hasDep = /依赖|先后|顺序|串行|并行|因果|前置步|必须先/i.test(t);
|
|
57
|
+
L('中间-依赖顺序', hasDep, hasDep ? '' : '缺少依赖/顺序说明');
|
|
58
|
+
|
|
59
|
+
// 有尾:收尾验证 + 清理残留 + 结束标记/复盘(防"只规划不落地")
|
|
60
|
+
const hasTailVerify = /现场自验|读回|自验|逐一|逐项|最终汇报|结束标记|复盘|确认.*落盘|全部.*完成/i.test(t);
|
|
61
|
+
L('有尾-收尾验证/自验', hasTailVerify, hasTailVerify ? '' : '缺少“收尾现场自验/最终读回”');
|
|
62
|
+
const hasClean = /清理|清残留|删除.*临时|无残留|no.*temp|临时产物.*清理|瘦身/i.test(t);
|
|
63
|
+
L('有尾-清理残留', hasClean, hasClean ? '' : '缺少“清理临时/残留产物”或无残留确认');
|
|
64
|
+
const hasEndmark = /__ORCH_DONE__|结束标记|输出.*结束|完成.*汇报|最后.*输出|EXIT/i.test(t);
|
|
65
|
+
L('有尾-结束标记/最终汇报', hasEndmark, hasEndmark ? '' : '缺少“结束标记/最终汇报”收尾');
|
|
66
|
+
|
|
67
|
+
// strict:额外要求目标+收尾验证+清理+步数 >= 1 全有
|
|
68
|
+
const strictAll = checks.every((c) => c.ok);
|
|
69
|
+
const ok = checks.every((c) => c.ok);
|
|
70
|
+
L('整体-有头有尾覆盖全链', ok, ok ? '' : '存在缺失项,禁止进入执行');
|
|
71
|
+
return { ok, checks };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { check, reportOk };
|
|
75
|
+
|
|
76
|
+
const isMain = process.argv[1] && process.argv[1].replace(/\\/g, '/').toLowerCase() === fileURLToPath(import.meta.url).replace(/\\/g, '/').toLowerCase();
|
|
77
|
+
if (isMain) {
|
|
78
|
+
if (!file) {
|
|
79
|
+
console.log('用法: node todo-list.mjs --check <清单文件> [--json] [--strict]');
|
|
80
|
+
process.exit(2);
|
|
81
|
+
}
|
|
82
|
+
if (!fs.existsSync(file)) {
|
|
83
|
+
console.error('清单文件不存在: ' + file);
|
|
84
|
+
process.exit(2);
|
|
85
|
+
}
|
|
86
|
+
const raw = fs.readFileSync(file, 'utf8');
|
|
87
|
+
const r = check(raw, STRICT);
|
|
88
|
+
reportOk(r.ok, r.checks);
|
|
89
|
+
}
|