@biaoo/tiangong-wiki 0.3.5 → 0.3.7
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 +2 -0
- package/README.zh-CN.md +2 -0
- package/SKILL.md +15 -1
- package/assets/config.example.env +18 -1
- package/dist/commands/check-config.js +2 -0
- package/dist/core/cli-env.js +21 -1
- package/dist/core/codex-workflow.js +69 -7
- package/dist/core/db.js +4 -0
- package/dist/core/embedding.js +2 -1
- package/dist/core/onboarding.js +76 -13
- package/dist/core/paths.js +34 -7
- package/dist/core/runtime.js +3 -3
- package/dist/core/sqlite-extensions.js +13 -9
- package/dist/core/sync.js +3 -3
- package/dist/core/vault-processing.js +242 -27
- package/dist/core/vault.js +14 -0
- package/dist/core/workflow-context.js +21 -0
- package/dist/core/workspace-skills.js +24 -7
- package/dist/daemon/git-journal.js +1 -0
- package/dist/operations/dashboard.js +4 -0
- package/dist/utils/process.js +18 -17
- package/mcp-server/package.json +1 -1
- package/package.json +10 -10
- package/references/cli-interface.md +8 -0
- package/references/troubleshooting.md +56 -7
package/README.md
CHANGED
|
@@ -86,6 +86,8 @@ tiangong-wiki sync # index Markdown pages
|
|
|
86
86
|
|
|
87
87
|
That means commands still work best from inside a workspace, but they can also run from outside the workspace after setup, or target a specific workspace explicitly with `--env-file`.
|
|
88
88
|
|
|
89
|
+
For automatic vault processing, new setup runs default to `WIKI_AGENT_AUTH_MODE=codex-login`, a dedicated Codex home under the current user's home directory, and `WIKI_AGENT_MODEL=gpt-5.5`. Before enabling that mode, run `CODEX_HOME="$HOME/.codex-tiangong-wiki" codex login` on macOS/Linux, or set `$env:CODEX_HOME = "$env:USERPROFILE\.codex-tiangong-wiki"` before `codex login` on Windows PowerShell.
|
|
90
|
+
|
|
89
91
|
```bash
|
|
90
92
|
tiangong-wiki find --type concept --status active # structured query
|
|
91
93
|
tiangong-wiki fts "Bayesian" # full-text search
|
package/README.zh-CN.md
CHANGED
|
@@ -86,6 +86,8 @@ tiangong-wiki sync # 索引 Markdown 文件
|
|
|
86
86
|
|
|
87
87
|
这意味着命令仍然最适合在 workspace 内执行;但 setup 之后,即使在 workspace 外运行,也可以通过默认配置正常工作,或者通过 `--env-file` 显式指定目标工作区。
|
|
88
88
|
|
|
89
|
+
如果启用自动 vault 处理,新的 setup 默认使用 `WIKI_AGENT_AUTH_MODE=codex-login`、当前用户 home 目录下的专用 Codex home 和 `WIKI_AGENT_MODEL=gpt-5.5`。启用前,macOS/Linux 执行 `CODEX_HOME="$HOME/.codex-tiangong-wiki" codex login`;Windows PowerShell 先设置 `$env:CODEX_HOME = "$env:USERPROFILE\.codex-tiangong-wiki"`,再执行 `codex login`。
|
|
90
|
+
|
|
89
91
|
```bash
|
|
90
92
|
tiangong-wiki find --type concept --status active # 结构化查询
|
|
91
93
|
tiangong-wiki fts "贝叶斯" # 全文搜索
|
package/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: tiangong-wiki-skill
|
|
3
|
-
description: "Use when you need to retrieve historically accumulated knowledge, methods, or behavioral patterns before answering or acting; when a conversation or workflow produces durable insights worth preserving for future reuse; or when
|
|
3
|
+
description: "Use when you need to retrieve historically accumulated knowledge, methods, or behavioral patterns before answering or acting; when a conversation or workflow produces durable insights worth preserving for future reuse; or when existing wiki content is outdated or incorrect. On Windows native shells or Codex automation, invoke the CLI as tiangong-wiki.cmd."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Wiki Skill
|
|
@@ -9,6 +9,20 @@ description: "Use when you need to retrieve historically accumulated knowledge,
|
|
|
9
9
|
|
|
10
10
|
Use the local wiki as the **durable knowledge layer** — not just for the current conversation, but for all future work. Query first, then read or edit the Markdown files that remain the source of truth.
|
|
11
11
|
|
|
12
|
+
## CLI Entrypoint
|
|
13
|
+
|
|
14
|
+
Use `tiangong-wiki <command>` on macOS, Linux, WSL, and Git Bash.
|
|
15
|
+
|
|
16
|
+
On Windows native shells such as PowerShell, Command Prompt, background daemon tasks, or Codex worker automation, use the npm command shim explicitly:
|
|
17
|
+
|
|
18
|
+
```powershell
|
|
19
|
+
tiangong-wiki.cmd doctor
|
|
20
|
+
tiangong-wiki.cmd sync
|
|
21
|
+
tiangong-wiki.cmd lint --format json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Do not invoke the suffixless `tiangong-wiki` executable from Windows native shells. npm also installs that shebang script for POSIX-like environments, but Windows may treat it as an unknown file and open the "choose an app" dialog instead of executing the CLI.
|
|
25
|
+
|
|
12
26
|
## When to Use
|
|
13
27
|
|
|
14
28
|
Activate this skill in three scenarios:
|
|
@@ -8,7 +8,24 @@ WIKI_SYNC_INTERVAL=86400
|
|
|
8
8
|
EMBEDDING_BASE_URL=https://api.openai.com/v1
|
|
9
9
|
EMBEDDING_API_KEY=your-embedding-api-key
|
|
10
10
|
EMBEDDING_MODEL=text-embedding-3-small
|
|
11
|
-
EMBEDDING_DIMENSIONS=
|
|
11
|
+
EMBEDDING_DIMENSIONS=1536
|
|
12
|
+
|
|
13
|
+
WIKI_AGENT_ENABLED=false
|
|
14
|
+
# For local Codex login auth, run on macOS/Linux:
|
|
15
|
+
# CODEX_HOME="$HOME/.codex-tiangong-wiki" codex login
|
|
16
|
+
# On Windows PowerShell:
|
|
17
|
+
# $env:CODEX_HOME = "$env:USERPROFILE\.codex-tiangong-wiki"; codex login
|
|
18
|
+
WIKI_AGENT_AUTH_MODE=codex-login
|
|
19
|
+
# Optional. Leave unset to use the current user's default: <home>/.codex-tiangong-wiki
|
|
20
|
+
# WIKI_AGENT_CODEX_HOME=/absolute/path/to/.codex-tiangong-wiki
|
|
21
|
+
#
|
|
22
|
+
# To use an API key instead of Codex login:
|
|
23
|
+
# WIKI_AGENT_AUTH_MODE=api-key
|
|
24
|
+
# WIKI_AGENT_BASE_URL=https://api.openai.com/v1
|
|
25
|
+
# WIKI_AGENT_API_KEY=sk-...
|
|
26
|
+
WIKI_AGENT_MODEL=gpt-5.5
|
|
27
|
+
WIKI_AGENT_BATCH_SIZE=5
|
|
28
|
+
WIKI_AGENT_SANDBOX_MODE=danger-full-access
|
|
12
29
|
|
|
13
30
|
VAULT_SOURCE=local
|
|
14
31
|
# When VAULT_SOURCE=synology, also set:
|
|
@@ -63,7 +63,9 @@ export function registerCheckConfigCommand(program) {
|
|
|
63
63
|
embeddingConfigured: embeddingClient !== null,
|
|
64
64
|
agentEnabled: wikiAgent.enabled,
|
|
65
65
|
agentConfigured: wikiAgent.configured,
|
|
66
|
+
agentAuthMode: wikiAgent.authMode,
|
|
66
67
|
agentBaseUrl: wikiAgent.baseUrl ?? "",
|
|
68
|
+
agentCodexHome: wikiAgent.codexHome ?? "",
|
|
67
69
|
agentModel: wikiAgent.model ?? "",
|
|
68
70
|
agentBatchSize: wikiAgent.batchSize,
|
|
69
71
|
agentWorkflowTimeoutSeconds: wikiAgent.workflowTimeoutSeconds,
|
package/dist/core/cli-env.js
CHANGED
|
@@ -27,7 +27,27 @@ function unquoteEnvValue(rawValue) {
|
|
|
27
27
|
return value;
|
|
28
28
|
}
|
|
29
29
|
if (value.startsWith('"') && value.endsWith('"')) {
|
|
30
|
-
|
|
30
|
+
const inner = value.slice(1, -1);
|
|
31
|
+
let output = "";
|
|
32
|
+
for (let index = 0; index < inner.length; index += 1) {
|
|
33
|
+
const current = inner[index];
|
|
34
|
+
if (current !== "\\" || index === inner.length - 1) {
|
|
35
|
+
output += current;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const next = inner[index + 1];
|
|
39
|
+
index += 1;
|
|
40
|
+
if (next === "n") {
|
|
41
|
+
output += "\n";
|
|
42
|
+
}
|
|
43
|
+
else if (next === '"' || next === "\\") {
|
|
44
|
+
output += next;
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
output += `\\${next}`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return output;
|
|
31
51
|
}
|
|
32
52
|
if (value.startsWith("'") && value.endsWith("'")) {
|
|
33
53
|
return value.slice(1, -1);
|
|
@@ -1,28 +1,89 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import {
|
|
2
|
+
import { mkdirSync } from "node:fs";
|
|
3
|
+
import childProcess from "node:child_process";
|
|
4
|
+
import { syncBuiltinESMExports } from "node:module";
|
|
3
5
|
import { readWorkflowResult } from "./workflow-result.js";
|
|
4
6
|
import { resolveAgentSettings } from "./paths.js";
|
|
5
7
|
import { readTextFileSync, writeTextFileSync } from "../utils/fs.js";
|
|
6
8
|
import { AppError } from "../utils/errors.js";
|
|
7
9
|
export const CODEX_WORKFLOW_VERSION = "2026-04-07";
|
|
10
|
+
const hiddenWindowsSpawnPatch = Symbol.for("tiangong-wiki.hiddenWindowsSpawnPatch");
|
|
11
|
+
function isSpawnOptions(value) {
|
|
12
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
13
|
+
}
|
|
14
|
+
function addHiddenWindowDefault(options) {
|
|
15
|
+
if (!isSpawnOptions(options)) {
|
|
16
|
+
return { windowsHide: true };
|
|
17
|
+
}
|
|
18
|
+
return { ...options, windowsHide: options.windowsHide ?? true };
|
|
19
|
+
}
|
|
20
|
+
export function createHiddenWindowsSpawn(spawnFn) {
|
|
21
|
+
return ((command, argsOrOptions, options) => {
|
|
22
|
+
if (Array.isArray(argsOrOptions)) {
|
|
23
|
+
return spawnFn(command, argsOrOptions, addHiddenWindowDefault(options));
|
|
24
|
+
}
|
|
25
|
+
if (isSpawnOptions(argsOrOptions) && options === undefined) {
|
|
26
|
+
return spawnFn(command, addHiddenWindowDefault(argsOrOptions));
|
|
27
|
+
}
|
|
28
|
+
if (argsOrOptions === undefined && options === undefined) {
|
|
29
|
+
return spawnFn(command, addHiddenWindowDefault(undefined));
|
|
30
|
+
}
|
|
31
|
+
return spawnFn(command, argsOrOptions, addHiddenWindowDefault(options));
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function installHiddenWindowsSpawnPatch(platform = process.platform) {
|
|
35
|
+
if (platform !== "win32") {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const currentSpawn = childProcess.spawn;
|
|
39
|
+
if (currentSpawn[hiddenWindowsSpawnPatch]) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const patchedSpawn = createHiddenWindowsSpawn(childProcess.spawn);
|
|
43
|
+
patchedSpawn[hiddenWindowsSpawnPatch] = true;
|
|
44
|
+
childProcess.spawn = patchedSpawn;
|
|
45
|
+
syncBuiltinESMExports();
|
|
46
|
+
}
|
|
47
|
+
let codexSdkModulePromise = null;
|
|
48
|
+
async function loadCodexSdk() {
|
|
49
|
+
// The SDK captures child_process.spawn during module import and currently
|
|
50
|
+
// does not expose windowsHide; install the Windows default before loading it.
|
|
51
|
+
installHiddenWindowsSpawnPatch();
|
|
52
|
+
codexSdkModulePromise ??= import("@openai/codex-sdk");
|
|
53
|
+
return codexSdkModulePromise;
|
|
54
|
+
}
|
|
8
55
|
function normalizeEnv(input) {
|
|
56
|
+
const agentSettings = resolveAgentSettings(input.env);
|
|
57
|
+
if (agentSettings.authMode === "codex-login" && agentSettings.codexHome) {
|
|
58
|
+
mkdirSync(agentSettings.codexHome, { recursive: true });
|
|
59
|
+
}
|
|
9
60
|
const normalized = {};
|
|
10
61
|
for (const [key, value] of Object.entries({
|
|
11
62
|
...process.env,
|
|
12
63
|
...input.env,
|
|
13
|
-
...(
|
|
64
|
+
...(agentSettings.authMode === "api-key" && agentSettings.apiKey && !input.env?.OPENAI_API_KEY
|
|
65
|
+
? { OPENAI_API_KEY: agentSettings.apiKey }
|
|
66
|
+
: {}),
|
|
67
|
+
...(agentSettings.authMode === "codex-login" && agentSettings.codexHome
|
|
68
|
+
? { CODEX_HOME: agentSettings.codexHome }
|
|
69
|
+
: {}),
|
|
14
70
|
})) {
|
|
15
71
|
if (typeof value === "string") {
|
|
16
72
|
normalized[key] = value;
|
|
17
73
|
}
|
|
18
74
|
}
|
|
75
|
+
if (agentSettings.authMode === "codex-login") {
|
|
76
|
+
delete normalized.OPENAI_API_KEY;
|
|
77
|
+
delete normalized.CODEX_API_KEY;
|
|
78
|
+
}
|
|
19
79
|
normalized.PATH = [input.skillArtifactsPath, normalized.PATH].filter(Boolean).join(path.delimiter);
|
|
20
80
|
return normalized;
|
|
21
81
|
}
|
|
22
|
-
function createCodexClient(input) {
|
|
82
|
+
async function createCodexClient(input) {
|
|
83
|
+
const agentSettings = resolveAgentSettings(input.env);
|
|
23
84
|
const env = normalizeEnv(input);
|
|
24
|
-
const baseUrl =
|
|
25
|
-
const apiKey =
|
|
85
|
+
const baseUrl = agentSettings.authMode === "api-key" ? agentSettings.baseUrl : null;
|
|
86
|
+
const apiKey = agentSettings.authMode === "api-key" ? agentSettings.apiKey : null;
|
|
26
87
|
const options = {
|
|
27
88
|
apiKey: apiKey || undefined,
|
|
28
89
|
env,
|
|
@@ -43,6 +104,7 @@ function createCodexClient(input) {
|
|
|
43
104
|
},
|
|
44
105
|
};
|
|
45
106
|
}
|
|
107
|
+
const { Codex } = await loadCodexSdk();
|
|
46
108
|
return new Codex(options);
|
|
47
109
|
}
|
|
48
110
|
function persistWorkflowThreadId(queueItemPath, threadId) {
|
|
@@ -117,7 +179,7 @@ export class CodexSdkWorkflowRunner {
|
|
|
117
179
|
// The SDK can only continue a thread by sending a new input, so queue retries
|
|
118
180
|
// must not automatically resume real workflow threads inline.
|
|
119
181
|
async startWorkflow(input) {
|
|
120
|
-
const codex = createCodexClient(input);
|
|
182
|
+
const codex = await createCodexClient(input);
|
|
121
183
|
const thread = codex.startThread({
|
|
122
184
|
model: input.model ?? undefined,
|
|
123
185
|
modelReasoningEffort: "low",
|
|
@@ -133,7 +195,7 @@ export class CodexSdkWorkflowRunner {
|
|
|
133
195
|
return { threadId, mode: "start" };
|
|
134
196
|
}
|
|
135
197
|
async resumeWorkflow(threadId, input) {
|
|
136
|
-
const codex = createCodexClient(input);
|
|
198
|
+
const codex = await createCodexClient(input);
|
|
137
199
|
const thread = codex.resumeThread(threadId, {
|
|
138
200
|
model: input.model ?? undefined,
|
|
139
201
|
modelReasoningEffort: "low",
|
package/dist/core/db.js
CHANGED
|
@@ -97,6 +97,8 @@ function ensureBaseTables(db, embeddingDimensions) {
|
|
|
97
97
|
queued_at TEXT NOT NULL,
|
|
98
98
|
claimed_at TEXT,
|
|
99
99
|
started_at TEXT,
|
|
100
|
+
heartbeat_at TEXT,
|
|
101
|
+
processing_owner_id TEXT,
|
|
100
102
|
processed_at TEXT,
|
|
101
103
|
result_page_id TEXT,
|
|
102
104
|
error_message TEXT,
|
|
@@ -133,6 +135,8 @@ function ensureBaseTables(db, embeddingDimensions) {
|
|
|
133
135
|
ensureTableColumns(db, "vault_processing_queue", {
|
|
134
136
|
claimed_at: "TEXT",
|
|
135
137
|
started_at: "TEXT",
|
|
138
|
+
heartbeat_at: "TEXT",
|
|
139
|
+
processing_owner_id: "TEXT",
|
|
136
140
|
thread_id: "TEXT",
|
|
137
141
|
workflow_version: "TEXT",
|
|
138
142
|
decision: "TEXT",
|
package/dist/core/embedding.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { sha256Text } from "../utils/fs.js";
|
|
2
2
|
import { AppError } from "../utils/errors.js";
|
|
3
|
+
export const DEFAULT_EMBEDDING_DIMENSIONS = 1536;
|
|
3
4
|
export class EmbeddingClient {
|
|
4
5
|
settings;
|
|
5
6
|
constructor(settings) {
|
|
@@ -9,7 +10,7 @@ export class EmbeddingClient {
|
|
|
9
10
|
const baseUrl = env.EMBEDDING_BASE_URL ?? env.OPENROUTER_BASE_URL;
|
|
10
11
|
const apiKey = env.EMBEDDING_API_KEY ?? env.OPENROUTER_API_KEY;
|
|
11
12
|
const model = env.EMBEDDING_MODEL ?? env.OPENROUTER_EMBEDDING_MODEL;
|
|
12
|
-
const rawDimensions = env.EMBEDDING_DIMENSIONS ??
|
|
13
|
+
const rawDimensions = env.EMBEDDING_DIMENSIONS ?? String(DEFAULT_EMBEDDING_DIMENSIONS);
|
|
13
14
|
if (!baseUrl || !apiKey || !model) {
|
|
14
15
|
return null;
|
|
15
16
|
}
|
package/dist/core/onboarding.js
CHANGED
|
@@ -4,9 +4,9 @@ import { fileURLToPath } from "node:url";
|
|
|
4
4
|
import { confirm, input, password, select } from "@inquirer/prompts";
|
|
5
5
|
import { DEFAULT_WIKI_ENV_FILE, getCliEnvironmentInfo, parseEnvFile, serializeEnvEntries } from "./cli-env.js";
|
|
6
6
|
import { resolveTemplateFilePath, loadConfig } from "./config.js";
|
|
7
|
-
import { EmbeddingClient } from "./embedding.js";
|
|
7
|
+
import { DEFAULT_EMBEDDING_DIMENSIONS, EmbeddingClient } from "./embedding.js";
|
|
8
8
|
import { writeGlobalConfig } from "./global-config.js";
|
|
9
|
-
import { parseVaultHashMode, parseWikiAgentSandboxMode, resolveAgentSettings } from "./paths.js";
|
|
9
|
+
import { DEFAULT_WIKI_AGENT_MODEL, defaultWikiAgentCodexHome, parseVaultHashMode, parseWikiAgentAuthMode, 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";
|
|
@@ -32,8 +32,10 @@ const MANAGED_ENV_KEYS = new Set([
|
|
|
32
32
|
"EMBEDDING_MODEL",
|
|
33
33
|
"EMBEDDING_DIMENSIONS",
|
|
34
34
|
"WIKI_AGENT_ENABLED",
|
|
35
|
+
"WIKI_AGENT_AUTH_MODE",
|
|
35
36
|
"WIKI_AGENT_BASE_URL",
|
|
36
37
|
"WIKI_AGENT_API_KEY",
|
|
38
|
+
"WIKI_AGENT_CODEX_HOME",
|
|
37
39
|
"WIKI_AGENT_MODEL",
|
|
38
40
|
"WIKI_AGENT_BATCH_SIZE",
|
|
39
41
|
"WIKI_AGENT_SANDBOX_MODE",
|
|
@@ -86,6 +88,17 @@ function safeAgentSandboxMode(rawValue) {
|
|
|
86
88
|
return "danger-full-access";
|
|
87
89
|
}
|
|
88
90
|
}
|
|
91
|
+
function safeAgentAuthMode(env) {
|
|
92
|
+
try {
|
|
93
|
+
if (env.WIKI_AGENT_AUTH_MODE && env.WIKI_AGENT_AUTH_MODE.trim()) {
|
|
94
|
+
return parseWikiAgentAuthMode(env.WIKI_AGENT_AUTH_MODE);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return env.WIKI_AGENT_API_KEY?.trim() ? "api-key" : "codex-login";
|
|
99
|
+
}
|
|
100
|
+
return env.WIKI_AGENT_API_KEY?.trim() ? "api-key" : "codex-login";
|
|
101
|
+
}
|
|
89
102
|
function safeBooleanFlag(rawValue, defaultValue) {
|
|
90
103
|
if (rawValue === undefined || rawValue.trim().length === 0) {
|
|
91
104
|
return defaultValue;
|
|
@@ -120,6 +133,12 @@ function validateWikiPath(rawValue) {
|
|
|
120
133
|
}
|
|
121
134
|
return null;
|
|
122
135
|
}
|
|
136
|
+
function validateAbsolutePath(rawValue, label) {
|
|
137
|
+
if (!path.isAbsolute(rawValue.trim())) {
|
|
138
|
+
return `${label} must be an absolute path.`;
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
123
142
|
class InquirerPromptDriver {
|
|
124
143
|
inputStream;
|
|
125
144
|
outputStream;
|
|
@@ -368,6 +387,7 @@ function getPathDefaults(env, cwd) {
|
|
|
368
387
|
const configPath = env.WIKI_CONFIG_PATH ? path.resolve(env.WIKI_CONFIG_PATH) : path.join(wikiRoot, "wiki.config.json");
|
|
369
388
|
const templatesPath = env.WIKI_TEMPLATES_PATH ? path.resolve(env.WIKI_TEMPLATES_PATH) : path.join(wikiRoot, "templates");
|
|
370
389
|
const defaultHashMode = vaultSource === "synology" ? "mtime" : "content";
|
|
390
|
+
const agentAuthMode = safeAgentAuthMode(env);
|
|
371
391
|
return {
|
|
372
392
|
envFilePath: env.WIKI_ENV_FILE ? path.resolve(cwd, env.WIKI_ENV_FILE) : path.join(cwd, DEFAULT_WIKI_ENV_FILE),
|
|
373
393
|
vaultSource,
|
|
@@ -388,11 +408,13 @@ function getPathDefaults(env, cwd) {
|
|
|
388
408
|
embeddingBaseUrl: env.EMBEDDING_BASE_URL ?? env.OPENROUTER_BASE_URL ?? "https://api.openai.com/v1",
|
|
389
409
|
embeddingApiKey: env.EMBEDDING_API_KEY ?? env.OPENROUTER_API_KEY ?? null,
|
|
390
410
|
embeddingModel: env.EMBEDDING_MODEL ?? env.OPENROUTER_EMBEDDING_MODEL ?? "text-embedding-3-small",
|
|
391
|
-
embeddingDimensions: env.EMBEDDING_DIMENSIONS ??
|
|
411
|
+
embeddingDimensions: env.EMBEDDING_DIMENSIONS ?? String(DEFAULT_EMBEDDING_DIMENSIONS),
|
|
392
412
|
agentEnabled: (env.WIKI_AGENT_ENABLED ?? "").trim().toLowerCase() === "true",
|
|
393
|
-
|
|
413
|
+
agentAuthMode,
|
|
414
|
+
agentBaseUrl: env.WIKI_AGENT_BASE_URL ?? (agentAuthMode === "api-key" ? "https://api.openai.com/v1" : null),
|
|
394
415
|
agentApiKey: env.WIKI_AGENT_API_KEY ?? null,
|
|
395
|
-
|
|
416
|
+
agentCodexHome: env.WIKI_AGENT_CODEX_HOME ?? defaultWikiAgentCodexHome(),
|
|
417
|
+
agentModel: env.WIKI_AGENT_MODEL ?? DEFAULT_WIKI_AGENT_MODEL,
|
|
396
418
|
agentBatchSize: env.WIKI_AGENT_BATCH_SIZE ?? "5",
|
|
397
419
|
agentSandboxMode: safeAgentSandboxMode(env.WIKI_AGENT_SANDBOX_MODE),
|
|
398
420
|
parserSkills: parseParserSkills(env.WIKI_PARSER_SKILLS, { strict: false }),
|
|
@@ -417,7 +439,7 @@ async function collectEmbeddingSettings(driver, ctx, defaults, env) {
|
|
|
417
439
|
required: true,
|
|
418
440
|
});
|
|
419
441
|
const embeddingModel = await promptText(driver, "EMBEDDING_MODEL", defaults.embeddingModel ?? "text-embedding-3-small", { required: true });
|
|
420
|
-
const embeddingDimensions = await promptText(driver, "EMBEDDING_DIMENSIONS", defaults.embeddingDimensions ??
|
|
442
|
+
const embeddingDimensions = await promptText(driver, "EMBEDDING_DIMENSIONS", defaults.embeddingDimensions ?? String(DEFAULT_EMBEDDING_DIMENSIONS), { validator: (value) => validateNonNegativeInteger(value, "EMBEDDING_DIMENSIONS") });
|
|
421
443
|
const shouldProbe = await promptYesNo(driver, "Probe the embedding endpoint now?", false);
|
|
422
444
|
if (shouldProbe) {
|
|
423
445
|
try {
|
|
@@ -457,19 +479,50 @@ async function collectAgentSettings(driver, ctx, defaults) {
|
|
|
457
479
|
if (!enabled) {
|
|
458
480
|
return {
|
|
459
481
|
agentEnabled: false,
|
|
482
|
+
agentAuthMode: null,
|
|
460
483
|
agentBaseUrl: null,
|
|
461
484
|
agentApiKey: null,
|
|
485
|
+
agentCodexHome: null,
|
|
462
486
|
agentModel: null,
|
|
463
487
|
agentBatchSize: null,
|
|
464
488
|
agentSandboxMode: null,
|
|
465
489
|
};
|
|
466
490
|
}
|
|
467
491
|
writeWarning(ctx.output, "Warning: danger-full-access grants full access to the runtime workspace.");
|
|
492
|
+
const agentAuthMode = await driver.select({
|
|
493
|
+
message: "WIKI_AGENT_AUTH_MODE",
|
|
494
|
+
defaultValue: defaults.agentAuthMode ?? "codex-login",
|
|
495
|
+
choices: [
|
|
496
|
+
{
|
|
497
|
+
value: "codex-login",
|
|
498
|
+
label: "codex-login",
|
|
499
|
+
description: "Use a local Codex ChatGPT login stored under WIKI_AGENT_CODEX_HOME.",
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
value: "api-key",
|
|
503
|
+
label: "api-key",
|
|
504
|
+
description: "Use WIKI_AGENT_API_KEY and optional WIKI_AGENT_BASE_URL.",
|
|
505
|
+
},
|
|
506
|
+
],
|
|
507
|
+
});
|
|
508
|
+
const authSettings = agentAuthMode === "api-key"
|
|
509
|
+
? {
|
|
510
|
+
agentBaseUrl: await promptText(driver, "WIKI_AGENT_BASE_URL", defaults.agentBaseUrl ?? "https://api.openai.com/v1", { validator: (value) => validateUrl(value, "WIKI_AGENT_BASE_URL") }),
|
|
511
|
+
agentApiKey: await promptPassword(driver, "WIKI_AGENT_API_KEY", defaults.agentApiKey ?? "", {
|
|
512
|
+
required: true,
|
|
513
|
+
}),
|
|
514
|
+
agentCodexHome: null,
|
|
515
|
+
}
|
|
516
|
+
: {
|
|
517
|
+
agentBaseUrl: null,
|
|
518
|
+
agentApiKey: null,
|
|
519
|
+
agentCodexHome: await promptText(driver, "WIKI_AGENT_CODEX_HOME", defaults.agentCodexHome ?? defaultWikiAgentCodexHome(), { validator: (value) => validateAbsolutePath(value, "WIKI_AGENT_CODEX_HOME") }),
|
|
520
|
+
};
|
|
468
521
|
return {
|
|
469
522
|
agentEnabled: true,
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
agentModel: await promptText(driver, "WIKI_AGENT_MODEL", defaults.agentModel ??
|
|
523
|
+
agentAuthMode,
|
|
524
|
+
...authSettings,
|
|
525
|
+
agentModel: await promptText(driver, "WIKI_AGENT_MODEL", defaults.agentModel ?? DEFAULT_WIKI_AGENT_MODEL, { required: true }),
|
|
473
526
|
agentBatchSize: await promptText(driver, "WIKI_AGENT_BATCH_SIZE", defaults.agentBatchSize ?? "5", { validator: (value) => validateNonNegativeInteger(value, "WIKI_AGENT_BATCH_SIZE") }),
|
|
474
527
|
agentSandboxMode: await driver.select({
|
|
475
528
|
message: "WIKI_AGENT_SANDBOX_MODE",
|
|
@@ -561,7 +614,13 @@ function buildSetupSummary(values) {
|
|
|
561
614
|
lines.push(` EMBEDDING_DIMENSIONS: ${values.embeddingDimensions}`);
|
|
562
615
|
}
|
|
563
616
|
if (values.agentEnabled) {
|
|
564
|
-
lines.push(`
|
|
617
|
+
lines.push(` WIKI_AGENT_AUTH_MODE: ${values.agentAuthMode}`);
|
|
618
|
+
if (values.agentAuthMode === "api-key") {
|
|
619
|
+
lines.push(` WIKI_AGENT_BASE_URL: ${values.agentBaseUrl}`);
|
|
620
|
+
}
|
|
621
|
+
if (values.agentAuthMode === "codex-login") {
|
|
622
|
+
lines.push(` WIKI_AGENT_CODEX_HOME: ${values.agentCodexHome}`);
|
|
623
|
+
}
|
|
565
624
|
lines.push(` WIKI_AGENT_MODEL: ${values.agentModel}`);
|
|
566
625
|
lines.push(` WIKI_AGENT_BATCH_SIZE: ${values.agentBatchSize}`);
|
|
567
626
|
lines.push(` WIKI_AGENT_SANDBOX_MODE: ${values.agentSandboxMode}`);
|
|
@@ -598,8 +657,10 @@ function writeSetupEnvFile(values) {
|
|
|
598
657
|
["EMBEDDING_MODEL", values.embeddingEnabled ? values.embeddingModel : null],
|
|
599
658
|
["EMBEDDING_DIMENSIONS", values.embeddingEnabled ? values.embeddingDimensions : null],
|
|
600
659
|
["WIKI_AGENT_ENABLED", values.agentEnabled ? "true" : "false"],
|
|
601
|
-
["
|
|
602
|
-
["
|
|
660
|
+
["WIKI_AGENT_AUTH_MODE", values.agentEnabled ? values.agentAuthMode : null],
|
|
661
|
+
["WIKI_AGENT_BASE_URL", values.agentEnabled && values.agentAuthMode === "api-key" ? values.agentBaseUrl : null],
|
|
662
|
+
["WIKI_AGENT_API_KEY", values.agentEnabled && values.agentAuthMode === "api-key" ? values.agentApiKey : null],
|
|
663
|
+
["WIKI_AGENT_CODEX_HOME", values.agentEnabled && values.agentAuthMode === "codex-login" ? values.agentCodexHome : null],
|
|
603
664
|
["WIKI_AGENT_MODEL", values.agentEnabled ? values.agentModel : null],
|
|
604
665
|
["WIKI_AGENT_BATCH_SIZE", values.agentEnabled ? values.agentBatchSize : null],
|
|
605
666
|
["WIKI_AGENT_SANDBOX_MODE", values.agentEnabled ? values.agentSandboxMode : null],
|
|
@@ -908,7 +969,9 @@ function inspectAgent(checks, env) {
|
|
|
908
969
|
collectDoctorCheck(checks, "error", "agent", `Automatic vault processing is enabled but missing: ${settings.missing.join(", ")}`, "Set the missing WIKI_AGENT_* values in `.wiki.env` or rerun `tiangong-wiki setup`.");
|
|
909
970
|
return;
|
|
910
971
|
}
|
|
911
|
-
collectDoctorCheck(checks, "ok", "agent",
|
|
972
|
+
collectDoctorCheck(checks, "ok", "agent", settings.authMode === "codex-login"
|
|
973
|
+
? `Automatic vault processing is enabled with model ${settings.model} via Codex login at ${settings.codexHome}.`
|
|
974
|
+
: `Automatic vault processing is enabled with model ${settings.model} via API key auth.`);
|
|
912
975
|
}
|
|
913
976
|
catch (error) {
|
|
914
977
|
const message = error instanceof Error ? error.message : String(error);
|
package/dist/core/paths.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { fileURLToPath } from "node:url";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
import { AppError } from "../utils/errors.js";
|
|
4
5
|
const TRUE_VALUES = new Set(["1", "true", "yes", "on"]);
|
|
5
6
|
const FALSE_VALUES = new Set(["0", "false", "no", "off"]);
|
|
7
|
+
export const DEFAULT_WIKI_AGENT_MODEL = "gpt-5.5";
|
|
6
8
|
function parseBooleanFlag(label, rawValue, defaultValue) {
|
|
7
9
|
if (rawValue === undefined) {
|
|
8
10
|
return defaultValue;
|
|
@@ -75,6 +77,19 @@ function requireAbsolutePath(label, rawValue) {
|
|
|
75
77
|
}
|
|
76
78
|
return path.resolve(rawValue);
|
|
77
79
|
}
|
|
80
|
+
function normalizeOptionalAbsolutePath(label, rawValue) {
|
|
81
|
+
const value = rawValue?.trim();
|
|
82
|
+
if (!value) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
if (!path.isAbsolute(value)) {
|
|
86
|
+
throw new AppError(`${label} must be an absolute path: ${rawValue}`, "config");
|
|
87
|
+
}
|
|
88
|
+
return path.resolve(value);
|
|
89
|
+
}
|
|
90
|
+
export function defaultWikiAgentCodexHome() {
|
|
91
|
+
return path.join(os.homedir(), ".codex-tiangong-wiki");
|
|
92
|
+
}
|
|
78
93
|
export function parseVaultHashMode(raw) {
|
|
79
94
|
const value = (raw ?? "content").trim().toLowerCase();
|
|
80
95
|
if (value === "content" || value === "mtime") {
|
|
@@ -89,6 +104,13 @@ export function parseWikiAgentBackend(raw) {
|
|
|
89
104
|
}
|
|
90
105
|
throw new AppError(`WIKI_AGENT_BACKEND must be "codex-workflow", got ${raw}`, "config");
|
|
91
106
|
}
|
|
107
|
+
export function parseWikiAgentAuthMode(raw) {
|
|
108
|
+
const value = (raw ?? "api-key").trim().toLowerCase();
|
|
109
|
+
if (value === "api-key" || value === "codex-login") {
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
throw new AppError(`WIKI_AGENT_AUTH_MODE must be "api-key" or "codex-login", got ${raw}`, "config");
|
|
113
|
+
}
|
|
92
114
|
export function parseWikiAgentSandboxMode(raw) {
|
|
93
115
|
const value = (raw ?? "danger-full-access").trim().toLowerCase();
|
|
94
116
|
if (value === "danger-full-access" || value === "workspace-write") {
|
|
@@ -98,28 +120,33 @@ export function parseWikiAgentSandboxMode(raw) {
|
|
|
98
120
|
}
|
|
99
121
|
export function resolveAgentSettings(env = process.env, options = {}) {
|
|
100
122
|
const enabled = parseBooleanFlag("WIKI_AGENT_ENABLED", env.WIKI_AGENT_ENABLED, false);
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const
|
|
123
|
+
const authMode = parseWikiAgentAuthMode(env.WIKI_AGENT_AUTH_MODE);
|
|
124
|
+
const rawBaseUrl = normalizeOptionalUrl(env.WIKI_AGENT_BASE_URL);
|
|
125
|
+
const baseUrl = authMode === "api-key" ? rawBaseUrl : null;
|
|
126
|
+
const rawApiKey = env.WIKI_AGENT_API_KEY?.trim() || null;
|
|
127
|
+
const apiKey = authMode === "api-key" ? rawApiKey : null;
|
|
128
|
+
const codexHome = authMode === "codex-login"
|
|
129
|
+
? normalizeOptionalAbsolutePath("WIKI_AGENT_CODEX_HOME", env.WIKI_AGENT_CODEX_HOME) ?? defaultWikiAgentCodexHome()
|
|
130
|
+
: null;
|
|
131
|
+
const model = env.WIKI_AGENT_MODEL?.trim() || DEFAULT_WIKI_AGENT_MODEL;
|
|
104
132
|
const batchSize = parseNonNegativeInteger(env.WIKI_AGENT_BATCH_SIZE, 5, "WIKI_AGENT_BATCH_SIZE");
|
|
105
133
|
const sandboxMode = parseWikiAgentSandboxMode(env.WIKI_AGENT_SANDBOX_MODE);
|
|
106
134
|
const workflowTimeoutSeconds = parsePositiveInteger(env.WIKI_WORKFLOW_TIMEOUT, 600, "WIKI_WORKFLOW_TIMEOUT");
|
|
107
135
|
const missing = [];
|
|
108
136
|
if (enabled) {
|
|
109
|
-
if (!apiKey) {
|
|
137
|
+
if (authMode === "api-key" && !apiKey) {
|
|
110
138
|
missing.push("WIKI_AGENT_API_KEY");
|
|
111
139
|
}
|
|
112
|
-
if (!model) {
|
|
113
|
-
missing.push("WIKI_AGENT_MODEL");
|
|
114
|
-
}
|
|
115
140
|
}
|
|
116
141
|
if (options.strict && enabled && missing.length > 0) {
|
|
117
142
|
throw new AppError(`WIKI_AGENT_ENABLED=true but missing required settings: ${missing.join(", ")}`, "config");
|
|
118
143
|
}
|
|
119
144
|
return {
|
|
120
145
|
enabled,
|
|
146
|
+
authMode,
|
|
121
147
|
baseUrl,
|
|
122
148
|
apiKey,
|
|
149
|
+
codexHome,
|
|
123
150
|
model,
|
|
124
151
|
batchSize,
|
|
125
152
|
sandboxMode,
|
package/dist/core/runtime.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { loadConfig } from "./config.js";
|
|
2
2
|
import { openDb } from "./db.js";
|
|
3
|
-
import { EmbeddingClient } from "./embedding.js";
|
|
3
|
+
import { DEFAULT_EMBEDDING_DIMENSIONS, EmbeddingClient } from "./embedding.js";
|
|
4
4
|
import { resolveRuntimePaths } from "./paths.js";
|
|
5
5
|
export function getEmbeddingDimensionFromEnv(env = process.env) {
|
|
6
|
-
const raw = env.EMBEDDING_DIMENSIONS ??
|
|
6
|
+
const raw = env.EMBEDDING_DIMENSIONS ?? String(DEFAULT_EMBEDDING_DIMENSIONS);
|
|
7
7
|
const value = Number.parseInt(raw, 10);
|
|
8
|
-
return Number.isFinite(value) && value > 0 ? value :
|
|
8
|
+
return Number.isFinite(value) && value > 0 ? value : DEFAULT_EMBEDDING_DIMENSIONS;
|
|
9
9
|
}
|
|
10
10
|
export function loadRuntimeConfig(env = process.env) {
|
|
11
11
|
const paths = resolveRuntimePaths(env);
|
|
@@ -23,21 +23,25 @@ function getExistingFtsSql(db) {
|
|
|
23
23
|
.get();
|
|
24
24
|
return row?.sql ?? null;
|
|
25
25
|
}
|
|
26
|
-
function
|
|
27
|
-
const byArch = SIMPLE_ASSET_MAP[
|
|
26
|
+
export function resolveBundledSimpleExtensionRelativePath(platform = process.platform, arch = process.arch) {
|
|
27
|
+
const byArch = SIMPLE_ASSET_MAP[platform];
|
|
28
28
|
if (!byArch) {
|
|
29
|
-
throw new AppError(`Bundled simple extension is not available for platform ${
|
|
30
|
-
platform
|
|
31
|
-
arch
|
|
29
|
+
throw new AppError(`Bundled simple extension is not available for platform ${platform}-${arch}.`, "config", {
|
|
30
|
+
platform,
|
|
31
|
+
arch,
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
|
-
const relativePath = byArch[
|
|
34
|
+
const relativePath = byArch[arch];
|
|
35
35
|
if (!relativePath) {
|
|
36
|
-
throw new AppError(`Bundled simple extension is not available for platform ${
|
|
37
|
-
platform
|
|
38
|
-
arch
|
|
36
|
+
throw new AppError(`Bundled simple extension is not available for platform ${platform}-${arch}.`, "config", {
|
|
37
|
+
platform,
|
|
38
|
+
arch,
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
|
+
return relativePath;
|
|
42
|
+
}
|
|
43
|
+
function resolveBundledSimpleExtensionPath(packageRoot) {
|
|
44
|
+
const relativePath = resolveBundledSimpleExtensionRelativePath();
|
|
41
45
|
const extensionPath = path.join(packageRoot, relativePath);
|
|
42
46
|
if (!pathExistsSync(extensionPath)) {
|
|
43
47
|
throw new AppError(`Bundled simple extension not found: ${extensionPath}`, "runtime", {
|
package/dist/core/sync.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { loadConfig } from "./config.js";
|
|
2
2
|
import { clearAllIndexedData, getMeta, openDb, resetVectorTable, setMetaValues, } from "./db.js";
|
|
3
|
-
import { EmbeddingClient } from "./embedding.js";
|
|
3
|
+
import { DEFAULT_EMBEDDING_DIMENSIONS, EmbeddingClient } from "./embedding.js";
|
|
4
4
|
import { applyChanges, scanPages, scanSpecificPages } from "./indexer.js";
|
|
5
5
|
import { resolveRuntimePaths } from "./paths.js";
|
|
6
6
|
import { collectVaultFiles, syncVaultIndex } from "./vault.js";
|
|
@@ -8,9 +8,9 @@ import { AppError } from "../utils/errors.js";
|
|
|
8
8
|
import { pathExistsSync } from "../utils/fs.js";
|
|
9
9
|
import { makeSyncId, toOffsetIso } from "../utils/time.js";
|
|
10
10
|
function getEmbeddingDimension(env) {
|
|
11
|
-
const raw = env.EMBEDDING_DIMENSIONS ??
|
|
11
|
+
const raw = env.EMBEDDING_DIMENSIONS ?? String(DEFAULT_EMBEDDING_DIMENSIONS);
|
|
12
12
|
const value = Number.parseInt(raw, 10);
|
|
13
|
-
return Number.isFinite(value) && value > 0 ? value :
|
|
13
|
+
return Number.isFinite(value) && value > 0 ? value : DEFAULT_EMBEDDING_DIMENSIONS;
|
|
14
14
|
}
|
|
15
15
|
function getEmbeddingTargets(db, embedAll, insertedIds, summaryChangedIds) {
|
|
16
16
|
const rows = db.prepare("SELECT rowid, id, summary_text AS summaryText, embedding_status AS embeddingStatus FROM pages").all();
|