@cyber-dash-tech/revela 0.17.9 → 0.17.11
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 +19 -3
- package/README.zh-CN.md +19 -3
- package/lib/log.ts +33 -22
- package/lib/refine/prompt-bridge.ts +25 -1
- package/lib/runtime/index.ts +16 -5
- package/package.json +1 -1
- package/plugins/revela/.codex-plugin/plugin.json +1 -1
- package/plugins/revela/.mcp.json +2 -5
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**English** | [中文](README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/@cyber-dash-tech/revela) [](LICENSE) [](https://www.npmjs.com/package/@cyber-dash-tech/revela) [](LICENSE) [](tests/) [](https://opencode.ai) [](https://bun.sh)
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<img src="assets/img/logo.png" alt="Revela" width="560" />
|
|
@@ -31,14 +31,30 @@ To install globally, add the same entry to `~/.config/opencode/opencode.json`.
|
|
|
31
31
|
|
|
32
32
|
### Codex
|
|
33
33
|
|
|
34
|
+
Requirements:
|
|
35
|
+
|
|
36
|
+
- The Codex CLI must be installed and the `codex` command must be available in your shell.
|
|
37
|
+
- Your environment must be able to run `npx`; Revela uses `npx -y @cyber-dash-tech/revela@0.17.11 mcp` to start the MCP server.
|
|
38
|
+
- For interactive Review actions, `codex exec` must also work because the Review UI uses it for Insight and Comment/Apply Fix requests.
|
|
39
|
+
|
|
40
|
+
Optional preflight:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
codex --version
|
|
44
|
+
codex exec --help
|
|
45
|
+
npx --version
|
|
46
|
+
```
|
|
47
|
+
|
|
34
48
|
Install Revela through the Codex Git marketplace:
|
|
35
49
|
|
|
36
50
|
```bash
|
|
37
|
-
codex plugin marketplace add https://github.com/cyber-dash-tech/revela --ref v0.17.
|
|
51
|
+
codex plugin marketplace add https://github.com/cyber-dash-tech/revela --ref v0.17.11
|
|
38
52
|
codex plugin add revela@revela
|
|
39
53
|
```
|
|
40
54
|
|
|
41
|
-
|
|
55
|
+
The Git marketplace install provides the Codex plugin shell, skills, hooks, and MCP configuration. When Codex starts the Revela MCP server for the first time, it runs `npx -y @cyber-dash-tech/revela@0.17.11 mcp` so npm can fetch the published package and its dependencies.
|
|
56
|
+
|
|
57
|
+
You do not need to run `bun install` inside the Codex marketplace clone.
|
|
42
58
|
|
|
43
59
|
Start a new Codex thread after installing so Codex loads the Revela skills, MCP tools, and hooks.
|
|
44
60
|
|
package/README.zh-CN.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | **中文**
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/@cyber-dash-tech/revela) [](LICENSE) [](https://www.npmjs.com/package/@cyber-dash-tech/revela) [](LICENSE) [](tests/) [](https://opencode.ai) [](https://bun.sh)
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
8
|
<img src="assets/img/logo.png" alt="Revela" width="560" />
|
|
@@ -31,14 +31,30 @@ Revela 可在 [OpenCode](https://opencode.ai) 和 Codex 中使用,把来源材
|
|
|
31
31
|
|
|
32
32
|
### Codex
|
|
33
33
|
|
|
34
|
+
环境要求:
|
|
35
|
+
|
|
36
|
+
- 需要已安装 Codex CLI,并且 shell 中可以执行 `codex`。
|
|
37
|
+
- 环境中需要可以执行 `npx`;Revela 会用 `npx -y @cyber-dash-tech/revela@0.17.11 mcp` 启动 MCP server。
|
|
38
|
+
- 如果使用 Review UI 的 Insight、Comment 或 Apply Fix,需要 `codex exec` 可用。
|
|
39
|
+
|
|
40
|
+
可选的安装前检查:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
codex --version
|
|
44
|
+
codex exec --help
|
|
45
|
+
npx --version
|
|
46
|
+
```
|
|
47
|
+
|
|
34
48
|
通过 Codex Git marketplace 安装 Revela:
|
|
35
49
|
|
|
36
50
|
```bash
|
|
37
|
-
codex plugin marketplace add https://github.com/cyber-dash-tech/revela --ref v0.17.
|
|
51
|
+
codex plugin marketplace add https://github.com/cyber-dash-tech/revela --ref v0.17.11
|
|
38
52
|
codex plugin add revela@revela
|
|
39
53
|
```
|
|
40
54
|
|
|
41
|
-
|
|
55
|
+
Git marketplace 安装的是 Codex plugin 壳、skills、hooks 和 MCP 配置。Codex 第一次启动 Revela MCP server 时,会运行 `npx -y @cyber-dash-tech/revela@0.17.11 mcp`,由 npm 获取已发布 package 及其 dependencies。
|
|
56
|
+
|
|
57
|
+
不需要在 Codex marketplace clone 里运行 `bun install`。
|
|
42
58
|
|
|
43
59
|
安装后开启一个新的 Codex thread,让 Codex 加载 Revela 的 skills、MCP tools 和 hooks。
|
|
44
60
|
|
package/lib/log.ts
CHANGED
|
@@ -1,28 +1,39 @@
|
|
|
1
|
-
import { Logger } from "tslog"
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Revela
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Set REVELA_DEBUG=1 to enable debug-level output (minLevel 2).
|
|
10
|
-
* Default minLevel is 3 (info) in production.
|
|
2
|
+
* Revela logger facade.
|
|
3
|
+
* Keep this module dependency-free so lightweight runtime tools can load from
|
|
4
|
+
* Codex Git marketplace checkouts that do not have package dependencies
|
|
5
|
+
* installed. Logging is intentionally silent by default because Revela often
|
|
6
|
+
* runs over stdio protocols where stderr noise is user-visible.
|
|
11
7
|
*/
|
|
12
|
-
|
|
8
|
+
type LogMethod = (message?: unknown, ...args: unknown[]) => void
|
|
9
|
+
|
|
10
|
+
export interface RevelaLogger {
|
|
11
|
+
silly: LogMethod
|
|
12
|
+
trace: LogMethod
|
|
13
|
+
debug: LogMethod
|
|
14
|
+
info: LogMethod
|
|
15
|
+
warn: LogMethod
|
|
16
|
+
error: LogMethod
|
|
17
|
+
fatal: LogMethod
|
|
18
|
+
getSubLogger(input?: { name?: string }): RevelaLogger
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const noop: LogMethod = () => {}
|
|
22
|
+
|
|
23
|
+
function createNoopLogger(_name = "revela"): RevelaLogger {
|
|
24
|
+
return {
|
|
25
|
+
silly: noop,
|
|
26
|
+
trace: noop,
|
|
27
|
+
debug: noop,
|
|
28
|
+
info: noop,
|
|
29
|
+
warn: noop,
|
|
30
|
+
error: noop,
|
|
31
|
+
fatal: noop,
|
|
32
|
+
getSubLogger: (input?: { name?: string }) => createNoopLogger(input?.name),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
13
35
|
|
|
14
|
-
export const log =
|
|
15
|
-
name: "revela",
|
|
16
|
-
minLevel,
|
|
17
|
-
type: "json",
|
|
18
|
-
hideLogPositionForProduction: true,
|
|
19
|
-
overwrite: {
|
|
20
|
-
transportJSON: (_logObj: unknown) => {
|
|
21
|
-
// Silenced: revela runs as an OpenCode plugin; writing to stderr
|
|
22
|
-
// pollutes the host terminal. Logs are intentionally suppressed.
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
})
|
|
36
|
+
export const log: RevelaLogger = createNoopLogger()
|
|
26
37
|
|
|
27
38
|
/**
|
|
28
39
|
* Create a child logger for a specific sub-module.
|
|
@@ -29,9 +29,11 @@ export interface CodexExecRunResult {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export type CodexExecRunner = (input: {
|
|
32
|
+
action: ReviewPromptAction
|
|
32
33
|
prompt: string
|
|
33
34
|
workspaceRoot: string
|
|
34
35
|
timeoutMs: number
|
|
36
|
+
sandboxMode: "read-only" | "workspace-write"
|
|
35
37
|
}) => Promise<CodexExecRunResult>
|
|
36
38
|
|
|
37
39
|
export function createOpenCodeReviewPromptBridge(client: any, sessionID: string): ReviewPromptBridge {
|
|
@@ -65,10 +67,13 @@ export function createCodexExecReviewPromptBridge(options: {
|
|
|
65
67
|
return {
|
|
66
68
|
kind: "codex-exec",
|
|
67
69
|
async send(input) {
|
|
70
|
+
const sandboxMode = input.action === "comment" ? "workspace-write" : "read-only"
|
|
68
71
|
const output = await runner({
|
|
72
|
+
action: input.action,
|
|
69
73
|
prompt: input.prompt,
|
|
70
74
|
workspaceRoot: input.workspaceRoot,
|
|
71
75
|
timeoutMs: input.timeoutMs ?? timeoutMs,
|
|
76
|
+
sandboxMode,
|
|
72
77
|
})
|
|
73
78
|
const raw = [output.stdout, output.stderr].filter(Boolean).join("\n")
|
|
74
79
|
if (output.exitCode !== 0) {
|
|
@@ -79,6 +84,14 @@ export function createCodexExecReviewPromptBridge(options: {
|
|
|
79
84
|
raw,
|
|
80
85
|
}
|
|
81
86
|
}
|
|
87
|
+
if (input.action === "comment" && isCodexWriteBlocked(raw)) {
|
|
88
|
+
return {
|
|
89
|
+
ok: false,
|
|
90
|
+
status: "failed",
|
|
91
|
+
error: "codex exec could not write the deck because its sandbox blocked file changes.",
|
|
92
|
+
raw,
|
|
93
|
+
}
|
|
94
|
+
}
|
|
82
95
|
if (input.action === "comment") return { ok: true, status: "completed", raw }
|
|
83
96
|
const result = extractInspectionResult(output.stdout)
|
|
84
97
|
if (!result) {
|
|
@@ -95,12 +108,14 @@ export function createCodexExecReviewPromptBridge(options: {
|
|
|
95
108
|
}
|
|
96
109
|
|
|
97
110
|
async function runCodexExec(input: {
|
|
111
|
+
action: ReviewPromptAction
|
|
98
112
|
prompt: string
|
|
99
113
|
workspaceRoot: string
|
|
100
114
|
timeoutMs: number
|
|
115
|
+
sandboxMode: "read-only" | "workspace-write"
|
|
101
116
|
}): Promise<CodexExecRunResult> {
|
|
102
117
|
return new Promise((resolve) => {
|
|
103
|
-
const child = spawn("codex", ["exec", "--json", "--ephemeral", "-C", input.workspaceRoot, input.prompt], {
|
|
118
|
+
const child = spawn("codex", ["exec", "--json", "--ephemeral", "--sandbox", input.sandboxMode, "-C", input.workspaceRoot, input.prompt], {
|
|
104
119
|
stdio: ["ignore", "pipe", "pipe"],
|
|
105
120
|
})
|
|
106
121
|
let stdout = ""
|
|
@@ -130,6 +145,15 @@ async function runCodexExec(input: {
|
|
|
130
145
|
})
|
|
131
146
|
}
|
|
132
147
|
|
|
148
|
+
function isCodexWriteBlocked(raw: string): boolean {
|
|
149
|
+
const text = raw.toLowerCase()
|
|
150
|
+
return (
|
|
151
|
+
(text.includes("patch rejected") && text.includes("read-only sandbox")) ||
|
|
152
|
+
text.includes("writing is blocked by read-only sandbox") ||
|
|
153
|
+
text.includes("blocked by read-only sandbox")
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
133
157
|
function extractInspectionResult(stdout: string): InspectionResult | undefined {
|
|
134
158
|
const direct = parseJson(stdout)
|
|
135
159
|
const fromDirect = findInspectionResult(direct)
|
package/lib/runtime/index.ts
CHANGED
|
@@ -7,14 +7,10 @@ import { computeNarrativeHash } from "../narrative-state/hash"
|
|
|
7
7
|
import { compileNarrativeVault } from "../narrative-vault/compile"
|
|
8
8
|
import { runNarrativeMarkdownQa, type MarkdownQaOptions } from "../narrative-vault/markdown-qa"
|
|
9
9
|
import { readDeckPlanArtifact } from "../narrative-state/deck-plan-artifact"
|
|
10
|
-
import { exportToPdf } from "../pdf/export"
|
|
11
|
-
import { exportToPptx } from "../pptx/export"
|
|
12
|
-
import { assertExportQAPassed } from "../qa/export-gate"
|
|
13
|
-
import { formatArtifactQAReport, runArtifactQA } from "../qa/artifact"
|
|
14
10
|
import { extractDesignClasses } from "../design/designs"
|
|
15
11
|
import { recordRenderedArtifact, workspaceRelative } from "../workspace-state/rendered-artifacts"
|
|
12
|
+
import type { ReviewDeckOpenInput, ReviewDeckReadInput } from "./review"
|
|
16
13
|
export { bindResearchFindings, evaluateResearchFindings, researchSave, researchTargets } from "./research"
|
|
17
|
-
export { reviewDeckOpen, reviewDeckRead } from "./review"
|
|
18
14
|
export { storyRead } from "./story"
|
|
19
15
|
|
|
20
16
|
export interface RuntimeWorkspaceInput {
|
|
@@ -91,6 +87,7 @@ export function createDeckFoundation(input: RuntimeDeckFoundationInput) {
|
|
|
91
87
|
}
|
|
92
88
|
|
|
93
89
|
export async function runDeckQa(input: RuntimeFileInput) {
|
|
90
|
+
const { formatArtifactQAReport, runArtifactQA } = await import("../qa/artifact")
|
|
94
91
|
const workspaceRoot = root(input.workspaceRoot)
|
|
95
92
|
const filePath = resolve(workspaceRoot, input.file)
|
|
96
93
|
let vocabulary
|
|
@@ -113,6 +110,8 @@ export async function runDeckQa(input: RuntimeFileInput) {
|
|
|
113
110
|
}
|
|
114
111
|
|
|
115
112
|
export async function exportPdf(input: RuntimeFileInput) {
|
|
113
|
+
const { exportToPdf } = await import("../pdf/export")
|
|
114
|
+
const { assertExportQAPassed } = await import("../qa/export-gate")
|
|
116
115
|
const workspaceRoot = root(input.workspaceRoot)
|
|
117
116
|
const filePath = resolve(workspaceRoot, input.file)
|
|
118
117
|
await assertExportQAPassed(filePath, { workspaceRoot })
|
|
@@ -127,6 +126,8 @@ export async function exportPdf(input: RuntimeFileInput) {
|
|
|
127
126
|
}
|
|
128
127
|
|
|
129
128
|
export async function exportPptx(input: RuntimeFileInput & { speakerNotes?: Array<string | null | undefined> }) {
|
|
129
|
+
const { exportToPptx } = await import("../pptx/export")
|
|
130
|
+
const { assertExportQAPassed } = await import("../qa/export-gate")
|
|
130
131
|
const workspaceRoot = root(input.workspaceRoot)
|
|
131
132
|
const filePath = resolve(workspaceRoot, input.file)
|
|
132
133
|
await assertExportQAPassed(filePath, { workspaceRoot })
|
|
@@ -140,6 +141,16 @@ export async function exportPptx(input: RuntimeFileInput & { speakerNotes?: Arra
|
|
|
140
141
|
return { ok: true, ...result }
|
|
141
142
|
}
|
|
142
143
|
|
|
144
|
+
export async function reviewDeckRead(input: ReviewDeckReadInput) {
|
|
145
|
+
const review = await import("./review")
|
|
146
|
+
return review.reviewDeckRead(input)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export async function reviewDeckOpen(input: ReviewDeckOpenInput) {
|
|
150
|
+
const review = await import("./review")
|
|
151
|
+
return review.reviewDeckOpen(input)
|
|
152
|
+
}
|
|
153
|
+
|
|
143
154
|
export function designList() {
|
|
144
155
|
seedBuiltinDesigns()
|
|
145
156
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "revela",
|
|
3
|
-
"version": "0.1.0+codex.
|
|
3
|
+
"version": "0.1.0+codex.20260524145000",
|
|
4
4
|
"description": "Use Revela in Codex to build trusted, traceable narrative decision artifacts from local sources and research.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "cyber-dash-tech",
|
package/plugins/revela/.mcp.json
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"mcpServers": {
|
|
3
3
|
"revela": {
|
|
4
|
-
"command": "
|
|
5
|
-
"args": [
|
|
6
|
-
"--eval",
|
|
7
|
-
"const fs = await import('fs');\nconst path = await import('path');\nconst { pathToFileURL } = await import('url');\nconst candidates = [];\nconst home = process.env.HOME || '';\nconst marketplaceNames = ['revela', 'revela-local'];\ncandidates.push(path.resolve(process.cwd(), 'bin/revela.ts'));\nconst configPath = path.join(home, '.codex', 'config.toml');\nif (fs.existsSync(configPath)) {\n const text = fs.readFileSync(configPath, 'utf-8');\n const sections = text.split(/\\n(?=\\s*\\[)/);\n for (const marketplaceName of marketplaceNames) {\n const section = sections.find((item) => item.trimStart().startsWith(`[marketplaces.${marketplaceName}]`));\n const match = section?.match(/^\\s*source\\s*=\\s*\"([^\"]+)\"/m);\n if (match && !/^[a-z][a-z0-9+.-]*:\\/\\//i.test(match[1])) candidates.push(path.join(match[1], 'bin/revela.ts'));\n }\n}\nfor (const marketplaceName of marketplaceNames) {\n candidates.push(path.join(home, '.codex', '.tmp', 'marketplaces', marketplaceName, 'bin/revela.ts'));\n}\nfor (const marketplaceName of marketplaceNames) {\n const cacheRoot = path.join(home, '.codex', 'plugins', 'cache', marketplaceName, 'revela');\n if (fs.existsSync(cacheRoot)) {\n for (const version of fs.readdirSync(cacheRoot).sort().reverse()) candidates.push(path.join(cacheRoot, version, 'bin/revela.ts'));\n }\n}\nconst cli = candidates.find((candidate) => fs.existsSync(candidate));\nif (!cli) {\n console.error(`Could not locate Revela CLI. Checked: ${candidates.join(', ')}`);\n process.exit(1);\n}\nprocess.env.REVELA_CLI_COMMAND = 'mcp';\nawait import(pathToFileURL(cli).href);"
|
|
8
|
-
]
|
|
4
|
+
"command": "npx",
|
|
5
|
+
"args": ["-y", "@cyber-dash-tech/revela@0.17.11", "mcp"]
|
|
9
6
|
}
|
|
10
7
|
}
|
|
11
8
|
}
|