@biaoo/tiangong-wiki 0.2.2 → 0.2.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.
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { Codex } from "@openai/codex-sdk";
|
|
3
3
|
import { readWorkflowResult } from "./workflow-result.js";
|
|
4
|
+
import { resolveAgentSettings } from "./paths.js";
|
|
4
5
|
import { readTextFileSync, writeTextFileSync } from "../utils/fs.js";
|
|
5
6
|
import { AppError } from "../utils/errors.js";
|
|
6
7
|
export const CODEX_WORKFLOW_VERSION = "2026-04-07";
|
|
@@ -85,16 +86,10 @@ async function runThread(thread, input) {
|
|
|
85
86
|
continue;
|
|
86
87
|
}
|
|
87
88
|
if (event.type === "turn.failed") {
|
|
88
|
-
throw
|
|
89
|
-
cause: event.error.message,
|
|
90
|
-
threadId: activeThreadId,
|
|
91
|
-
});
|
|
89
|
+
throw classifyWorkflowRuntimeError("Codex workflow turn failed", event.error.message, activeThreadId);
|
|
92
90
|
}
|
|
93
91
|
if (event.type === "error") {
|
|
94
|
-
throw
|
|
95
|
-
cause: event.message,
|
|
96
|
-
threadId: activeThreadId,
|
|
97
|
-
});
|
|
92
|
+
throw classifyWorkflowRuntimeError("Codex workflow stream failed", event.message, activeThreadId);
|
|
98
93
|
}
|
|
99
94
|
}
|
|
100
95
|
}
|
|
@@ -103,10 +98,7 @@ async function runThread(thread, input) {
|
|
|
103
98
|
throw error;
|
|
104
99
|
}
|
|
105
100
|
const message = error instanceof Error ? error.message : String(error);
|
|
106
|
-
throw
|
|
107
|
-
cause: message,
|
|
108
|
-
threadId: activeThreadId,
|
|
109
|
-
});
|
|
101
|
+
throw classifyWorkflowRuntimeError("Codex workflow turn failed", message, activeThreadId);
|
|
110
102
|
}
|
|
111
103
|
if (!activeThreadId && thread.id) {
|
|
112
104
|
activeThreadId = thread.id;
|
|
@@ -118,6 +110,10 @@ async function runThread(thread, input) {
|
|
|
118
110
|
return activeThreadId;
|
|
119
111
|
}
|
|
120
112
|
export class CodexSdkWorkflowRunner {
|
|
113
|
+
options;
|
|
114
|
+
constructor(options = {}) {
|
|
115
|
+
this.options = options;
|
|
116
|
+
}
|
|
121
117
|
// The SDK can only continue a thread by sending a new input, so queue retries
|
|
122
118
|
// must not automatically resume real workflow threads inline.
|
|
123
119
|
async startWorkflow(input) {
|
|
@@ -127,7 +123,7 @@ export class CodexSdkWorkflowRunner {
|
|
|
127
123
|
modelReasoningEffort: "low",
|
|
128
124
|
workingDirectory: input.workspaceRoot,
|
|
129
125
|
skipGitRepoCheck: true,
|
|
130
|
-
sandboxMode: "
|
|
126
|
+
sandboxMode: this.options.sandboxMode ?? "danger-full-access",
|
|
131
127
|
networkAccessEnabled: true,
|
|
132
128
|
approvalPolicy: "never",
|
|
133
129
|
webSearchMode: "disabled",
|
|
@@ -143,7 +139,7 @@ export class CodexSdkWorkflowRunner {
|
|
|
143
139
|
modelReasoningEffort: "low",
|
|
144
140
|
workingDirectory: input.workspaceRoot,
|
|
145
141
|
skipGitRepoCheck: true,
|
|
146
|
-
sandboxMode: "
|
|
142
|
+
sandboxMode: this.options.sandboxMode ?? "danger-full-access",
|
|
147
143
|
networkAccessEnabled: true,
|
|
148
144
|
approvalPolicy: "never",
|
|
149
145
|
webSearchMode: "disabled",
|
|
@@ -229,5 +225,31 @@ export function createDefaultWorkflowRunner(env = process.env) {
|
|
|
229
225
|
const delayMs = Number.parseInt(env.WIKI_TEST_FAKE_WORKFLOW_DELAY_MS ?? "0", 10) || 0;
|
|
230
226
|
return createSkipOnlyTestWorkflowRunner({ delayMs, mode: "delay-skip" });
|
|
231
227
|
}
|
|
232
|
-
return new CodexSdkWorkflowRunner(
|
|
228
|
+
return new CodexSdkWorkflowRunner({
|
|
229
|
+
sandboxMode: resolveAgentSettings(env).sandboxMode,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
function isSandboxStartupFailure(message) {
|
|
233
|
+
const normalized = message.toLowerCase();
|
|
234
|
+
return (normalized.includes("bwrap") ||
|
|
235
|
+
normalized.includes("bubblewrap") ||
|
|
236
|
+
normalized.includes("uid map") ||
|
|
237
|
+
normalized.includes("uid_map") ||
|
|
238
|
+
normalized.includes("gid map") ||
|
|
239
|
+
normalized.includes("gid_map") ||
|
|
240
|
+
normalized.includes("unshare") ||
|
|
241
|
+
normalized.includes("operation not permitted"));
|
|
242
|
+
}
|
|
243
|
+
function classifyWorkflowRuntimeError(baseMessage, cause, threadId) {
|
|
244
|
+
if (isSandboxStartupFailure(cause)) {
|
|
245
|
+
return new AppError("Codex workflow sandbox failed to initialize", "runtime", {
|
|
246
|
+
cause,
|
|
247
|
+
threadId,
|
|
248
|
+
phase: "sandbox",
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
return new AppError(baseMessage, "runtime", {
|
|
252
|
+
cause,
|
|
253
|
+
threadId,
|
|
254
|
+
});
|
|
233
255
|
}
|
package/dist/core/onboarding.js
CHANGED
|
@@ -6,7 +6,7 @@ import { DEFAULT_WIKI_ENV_FILE, getCliEnvironmentInfo, parseEnvFile, serializeEn
|
|
|
6
6
|
import { resolveTemplateFilePath, loadConfig } from "./config.js";
|
|
7
7
|
import { EmbeddingClient } from "./embedding.js";
|
|
8
8
|
import { writeGlobalConfig } from "./global-config.js";
|
|
9
|
-
import { parseVaultHashMode, resolveAgentSettings } from "./paths.js";
|
|
9
|
+
import { parseVaultHashMode, parseWikiAgentSandboxMode, resolveAgentSettings } from "./paths.js";
|
|
10
10
|
import { loadSynologyConfigFromEnv, normalizeSynologyRemotePath, withSynologyClient } from "./synology.js";
|
|
11
11
|
import { ensureWikiSkillInstall, formatParserSkills, inspectSkillInstall, installParserSkill, OPTIONAL_PARSER_SKILLS, parseParserSkillSelection, parseParserSkills, resolveWorkspaceRootFromWikiPath, resolveWorkspaceSkillPath, resolveWorkspaceSkillPaths, } from "./workspace-skills.js";
|
|
12
12
|
import { scaffoldWorkspaceAssets } from "./workspace-bootstrap.js";
|
|
@@ -36,11 +36,16 @@ const MANAGED_ENV_KEYS = new Set([
|
|
|
36
36
|
"WIKI_AGENT_API_KEY",
|
|
37
37
|
"WIKI_AGENT_MODEL",
|
|
38
38
|
"WIKI_AGENT_BATCH_SIZE",
|
|
39
|
+
"WIKI_AGENT_SANDBOX_MODE",
|
|
39
40
|
"WIKI_PARSER_SKILLS",
|
|
40
41
|
]);
|
|
41
42
|
function writeSection(output, title) {
|
|
42
43
|
output.write(`\n${title}\n`);
|
|
43
44
|
}
|
|
45
|
+
function writeWarning(output, message) {
|
|
46
|
+
const isTty = "isTTY" in output && output.isTTY;
|
|
47
|
+
output.write(isTty ? `\x1b[31m${message}\x1b[0m\n` : `${message}\n`);
|
|
48
|
+
}
|
|
44
49
|
function resolvePackageRoot(packageRoot) {
|
|
45
50
|
return packageRoot ?? path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
46
51
|
}
|
|
@@ -73,6 +78,14 @@ function safeVaultHashMode(rawValue, defaultValue) {
|
|
|
73
78
|
return defaultValue;
|
|
74
79
|
}
|
|
75
80
|
}
|
|
81
|
+
function safeAgentSandboxMode(rawValue) {
|
|
82
|
+
try {
|
|
83
|
+
return parseWikiAgentSandboxMode(rawValue);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return "danger-full-access";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
76
89
|
function safeBooleanFlag(rawValue, defaultValue) {
|
|
77
90
|
if (rawValue === undefined || rawValue.trim().length === 0) {
|
|
78
91
|
return defaultValue;
|
|
@@ -381,6 +394,7 @@ function getPathDefaults(env, cwd) {
|
|
|
381
394
|
agentApiKey: env.WIKI_AGENT_API_KEY ?? null,
|
|
382
395
|
agentModel: env.WIKI_AGENT_MODEL ?? null,
|
|
383
396
|
agentBatchSize: env.WIKI_AGENT_BATCH_SIZE ?? "5",
|
|
397
|
+
agentSandboxMode: safeAgentSandboxMode(env.WIKI_AGENT_SANDBOX_MODE),
|
|
384
398
|
parserSkills: parseParserSkills(env.WIKI_PARSER_SKILLS, { strict: false }),
|
|
385
399
|
};
|
|
386
400
|
}
|
|
@@ -447,14 +461,32 @@ async function collectAgentSettings(driver, ctx, defaults) {
|
|
|
447
461
|
agentApiKey: null,
|
|
448
462
|
agentModel: null,
|
|
449
463
|
agentBatchSize: null,
|
|
464
|
+
agentSandboxMode: null,
|
|
450
465
|
};
|
|
451
466
|
}
|
|
467
|
+
writeWarning(ctx.output, "Warning: danger-full-access grants full access to the runtime workspace.");
|
|
452
468
|
return {
|
|
453
469
|
agentEnabled: true,
|
|
454
470
|
agentBaseUrl: await promptText(driver, "WIKI_AGENT_BASE_URL", defaults.agentBaseUrl ?? "https://api.openai.com/v1", { validator: (value) => validateUrl(value, "WIKI_AGENT_BASE_URL") }),
|
|
455
471
|
agentApiKey: await promptPassword(driver, "WIKI_AGENT_API_KEY", defaults.agentApiKey ?? "", { required: true }),
|
|
456
472
|
agentModel: await promptText(driver, "WIKI_AGENT_MODEL", defaults.agentModel ?? "", { required: true }),
|
|
457
473
|
agentBatchSize: await promptText(driver, "WIKI_AGENT_BATCH_SIZE", defaults.agentBatchSize ?? "5", { validator: (value) => validateNonNegativeInteger(value, "WIKI_AGENT_BATCH_SIZE") }),
|
|
474
|
+
agentSandboxMode: await driver.select({
|
|
475
|
+
message: "WIKI_AGENT_SANDBOX_MODE",
|
|
476
|
+
defaultValue: defaults.agentSandboxMode ?? "danger-full-access",
|
|
477
|
+
choices: [
|
|
478
|
+
{
|
|
479
|
+
value: "danger-full-access",
|
|
480
|
+
label: "danger-full-access",
|
|
481
|
+
description: "Full access to the runtime workspace. Default.",
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
value: "workspace-write",
|
|
485
|
+
label: "workspace-write",
|
|
486
|
+
description: "Use Codex workspace-write sandbox when the host supports it.",
|
|
487
|
+
},
|
|
488
|
+
],
|
|
489
|
+
}),
|
|
458
490
|
};
|
|
459
491
|
}
|
|
460
492
|
async function collectSynologySettings(driver, ctx, defaults) {
|
|
@@ -532,6 +564,7 @@ function buildSetupSummary(values) {
|
|
|
532
564
|
lines.push(` WIKI_AGENT_BASE_URL: ${values.agentBaseUrl}`);
|
|
533
565
|
lines.push(` WIKI_AGENT_MODEL: ${values.agentModel}`);
|
|
534
566
|
lines.push(` WIKI_AGENT_BATCH_SIZE: ${values.agentBatchSize}`);
|
|
567
|
+
lines.push(` WIKI_AGENT_SANDBOX_MODE: ${values.agentSandboxMode}`);
|
|
535
568
|
}
|
|
536
569
|
if (values.vaultSource === "synology") {
|
|
537
570
|
lines.push(` SYNOLOGY_BASE_URL: ${values.synologyBaseUrl}`);
|
|
@@ -569,6 +602,7 @@ function writeSetupEnvFile(values) {
|
|
|
569
602
|
["WIKI_AGENT_API_KEY", values.agentEnabled ? values.agentApiKey : null],
|
|
570
603
|
["WIKI_AGENT_MODEL", values.agentEnabled ? values.agentModel : null],
|
|
571
604
|
["WIKI_AGENT_BATCH_SIZE", values.agentEnabled ? values.agentBatchSize : null],
|
|
605
|
+
["WIKI_AGENT_SANDBOX_MODE", values.agentEnabled ? values.agentSandboxMode : null],
|
|
572
606
|
["WIKI_PARSER_SKILLS", formatParserSkills(values.parserSkills)],
|
|
573
607
|
];
|
|
574
608
|
const body = [
|
package/dist/core/paths.js
CHANGED
|
@@ -89,12 +89,20 @@ export function parseWikiAgentBackend(raw) {
|
|
|
89
89
|
}
|
|
90
90
|
throw new AppError(`WIKI_AGENT_BACKEND must be "codex-workflow", got ${raw}`, "config");
|
|
91
91
|
}
|
|
92
|
+
export function parseWikiAgentSandboxMode(raw) {
|
|
93
|
+
const value = (raw ?? "danger-full-access").trim().toLowerCase();
|
|
94
|
+
if (value === "danger-full-access" || value === "workspace-write") {
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
throw new AppError(`WIKI_AGENT_SANDBOX_MODE must be "danger-full-access" or "workspace-write", got ${raw}`, "config");
|
|
98
|
+
}
|
|
92
99
|
export function resolveAgentSettings(env = process.env, options = {}) {
|
|
93
100
|
const enabled = parseBooleanFlag("WIKI_AGENT_ENABLED", env.WIKI_AGENT_ENABLED, false);
|
|
94
101
|
const baseUrl = normalizeOptionalUrl(env.WIKI_AGENT_BASE_URL);
|
|
95
102
|
const apiKey = env.WIKI_AGENT_API_KEY?.trim() || null;
|
|
96
103
|
const model = env.WIKI_AGENT_MODEL?.trim() || null;
|
|
97
104
|
const batchSize = parseNonNegativeInteger(env.WIKI_AGENT_BATCH_SIZE, 5, "WIKI_AGENT_BATCH_SIZE");
|
|
105
|
+
const sandboxMode = parseWikiAgentSandboxMode(env.WIKI_AGENT_SANDBOX_MODE);
|
|
98
106
|
const workflowTimeoutSeconds = parsePositiveInteger(env.WIKI_WORKFLOW_TIMEOUT, 600, "WIKI_WORKFLOW_TIMEOUT");
|
|
99
107
|
const missing = [];
|
|
100
108
|
if (enabled) {
|
|
@@ -114,6 +122,7 @@ export function resolveAgentSettings(env = process.env, options = {}) {
|
|
|
114
122
|
apiKey,
|
|
115
123
|
model,
|
|
116
124
|
batchSize,
|
|
125
|
+
sandboxMode,
|
|
117
126
|
workflowTimeoutSeconds,
|
|
118
127
|
configured: enabled && missing.length === 0,
|
|
119
128
|
missing,
|
|
@@ -148,6 +148,11 @@ export function readWorkflowResult(resultPath) {
|
|
|
148
148
|
fail(`Workflow result not found: ${resultPath}`);
|
|
149
149
|
}
|
|
150
150
|
const rawText = readTextFileSync(resultPath);
|
|
151
|
+
if (!rawText.trim()) {
|
|
152
|
+
fail("Workflow result is empty", {
|
|
153
|
+
resultPath,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
151
156
|
let parsed;
|
|
152
157
|
try {
|
|
153
158
|
parsed = JSON.parse(rawText);
|
package/package.json
CHANGED
|
@@ -73,8 +73,11 @@ The agent uses [Codex SDK](https://www.npmjs.com/package/@openai/codex-sdk) to p
|
|
|
73
73
|
| `WIKI_AGENT_API_KEY` | If enabled | API key for the LLM provider |
|
|
74
74
|
| `WIKI_AGENT_MODEL` | No | Model name (e.g. `gpt-5.4`, `Qwen/Qwen3.5-397B-A17B-GPTQ-Int4`) |
|
|
75
75
|
| `WIKI_AGENT_BATCH_SIZE` | No | Max concurrent vault items per batch (default: `5`) |
|
|
76
|
+
| `WIKI_AGENT_SANDBOX_MODE` | No | Codex sandbox mode: `danger-full-access` (default) or `workspace-write` |
|
|
76
77
|
| `WIKI_PARSER_SKILLS` | No | Comma-separated parser skill list (e.g. `pdf,docx,pptx,xlsx`) |
|
|
77
78
|
|
|
79
|
+
`tiangong-wiki setup` now prompts for `WIKI_AGENT_SANDBOX_MODE` when automatic vault processing is enabled. The default is `danger-full-access`, and the setup wizard highlights that this mode grants full runtime access.
|
|
80
|
+
|
|
78
81
|
---
|
|
79
82
|
|
|
80
83
|
## Common Issues
|
|
@@ -102,6 +105,10 @@ Always run `tiangong-wiki lint --path <page-id> --format json` after mutations.
|
|
|
102
105
|
|
|
103
106
|
Parser skills must be installed under `<workspace-root>/.agents/skills/`. Run `tiangong-wiki skill` to inspect installed skills. Use `tiangong-wiki skill update --all` to update.
|
|
104
107
|
|
|
108
|
+
### Codex workflow sandbox fails to initialize
|
|
109
|
+
|
|
110
|
+
If the agent workflow fails with `bwrap`, `unshare`, `uid_map`, or similar sandbox startup errors, switch `WIKI_AGENT_SANDBOX_MODE` to `danger-full-access`. Use `workspace-write` only when you explicitly want that sandbox mode and know the host supports it.
|
|
111
|
+
|
|
105
112
|
---
|
|
106
113
|
|
|
107
114
|
## LLM Provider Setup
|