@lumi-ai-lab/harness-data 0.0.20 → 0.0.22
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 -2
- package/package.json +1 -1
- package/src/commands/doctor.js +6 -4
- package/src/commands/install.js +12 -5
- package/src/commands/update.js +50 -12
- package/src/lib/config.js +8 -2
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ or:
|
|
|
24
24
|
GITHUB_TOKEN=... npx @lumi-ai-lab/harness-data install
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
Without a GitHub token, the installer interactively asks for local absolute paths to `cas-cli`, `qdm-indicators-cli`, `qdm-cmr-cli`, and `harness-data-wikis`. CAS username and password are always collected interactively.
|
|
27
|
+
Without a GitHub token, the installer interactively asks for local absolute paths to `cas-cli`, `qdm-indicators-cli`, `qdm-cmr-cli`, `qdm-sql-cli`, and `harness-data-wikis`. CAS username and password are always collected interactively.
|
|
28
28
|
|
|
29
29
|
Update an existing runtime interactively:
|
|
30
30
|
|
|
@@ -38,6 +38,6 @@ Diagnose a runtime:
|
|
|
38
38
|
npx @lumi-ai-lab/harness-data doctor
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
The runtime is assembled from the `harness-data` runtime bundle, platform-specific CLI Release assets, `harness-data-wikis`, generated local config, CAS credentials, and selected Agent symlinks.
|
|
41
|
+
The runtime is assembled from the `harness-data` runtime bundle, platform-specific CLI Release assets, `harness-data-wikis`, generated local config, CAS credentials, and selected Agent symlinks. SQL CLI tokens are fetched through `cas-cli token --app rtp`.
|
|
42
42
|
|
|
43
43
|
`--agent` supports `claude`, `codex`, `pi`, `openclaw`, `hermes`, `both`, and `all`; the default is `all`. `both` installs Claude + Codex, while `all` installs Claude + Codex + Pi + OpenClaw + Hermes.
|
package/package.json
CHANGED
package/src/commands/doctor.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { run } from "../lib/exec.js";
|
|
4
4
|
import { findWorkspaceDir } from "../lib/paths.js";
|
|
5
5
|
import { binaryName } from "../lib/platform.js";
|
|
6
|
-
import { concreteAgentNames } from "../lib/config.js";
|
|
6
|
+
import { concreteAgentNames, qdmCliBinaries } from "../lib/config.js";
|
|
7
7
|
|
|
8
8
|
function existsExecutable(file) {
|
|
9
9
|
try {
|
|
@@ -36,8 +36,9 @@ function configPathsValid(workspace) {
|
|
|
36
36
|
const file = path.join(workspace, "config", "qdm-cli-paths.env");
|
|
37
37
|
if (!fs.existsSync(file)) return false;
|
|
38
38
|
const content = fs.readFileSync(file, "utf8");
|
|
39
|
-
const
|
|
40
|
-
|
|
39
|
+
const required = ["QDM_CMR_CLI", "QDM_INDICATORS_CLI", "QDM_SQL_CLI", "QDM_CAS_CLI"];
|
|
40
|
+
const values = new Map([...content.matchAll(/^export\s+([A-Z0-9_]+)="([^"]+)"/gm)].map((match) => [match[1], match[2]]));
|
|
41
|
+
return required.every((name) => values.has(name) && fs.existsSync(values.get(name)));
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
function casCredentialsValid(dir) {
|
|
@@ -66,7 +67,7 @@ export async function collectDoctor(workspace, options = {}) {
|
|
|
66
67
|
add("wikis/reports", fs.existsSync(path.join(workspace, "wikis", "reports")));
|
|
67
68
|
add("wikis/dims", fs.existsSync(path.join(workspace, "wikis", "dims")));
|
|
68
69
|
add("wikis/rules", fs.existsSync(path.join(workspace, "wikis", "rules")));
|
|
69
|
-
for (const binary of
|
|
70
|
+
for (const binary of qdmCliBinaries) {
|
|
70
71
|
add(`bin/${binary}`, existsExecutable(path.join(workspace, "bin", binaryName(binary))));
|
|
71
72
|
}
|
|
72
73
|
add("config/harness-config.yaml", fs.existsSync(path.join(workspace, "config", "harness-config.yaml")));
|
|
@@ -75,6 +76,7 @@ export async function collectDoctor(workspace, options = {}) {
|
|
|
75
76
|
add("CAS credentials file", casCredentialsValid(casConfigDir), casConfigDir);
|
|
76
77
|
add("CMR token", await tokenCheck(workspace, "qdm-cmr-cli", env));
|
|
77
78
|
add("Indicators token", await tokenCheck(workspace, "qdm-indicators-cli", env));
|
|
79
|
+
add("SQL token", await tokenCheck(workspace, "qdm-sql-cli", env));
|
|
78
80
|
add("Agent hook", concreteAgentNames.some((name) => agentOk(workspace, name)));
|
|
79
81
|
for (const name of ["openclaw", "hermes"]) {
|
|
80
82
|
if (fs.existsSync(path.join(workspace, `.${name}`))) {
|
package/src/commands/install.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { commandExists, run } from "../lib/exec.js";
|
|
4
|
-
import { writeLocalConfig, linkAgents } from "../lib/config.js";
|
|
4
|
+
import { localPathToolNames, writeLocalConfig, linkAgents } from "../lib/config.js";
|
|
5
5
|
import { ask, askSecret, chooseAgent } from "../lib/prompt.js";
|
|
6
6
|
import { readUserState, resolveWorkspaceDir, writeState } from "../lib/paths.js";
|
|
7
7
|
import { installToolsFromManifest, manifestDigest, readManifest } from "../lib/manifest.js";
|
|
@@ -156,7 +156,7 @@ async function installLocalTools(runtimeDir, options = {}) {
|
|
|
156
156
|
const binDir = path.join(runtimeDir, "bin");
|
|
157
157
|
fs.mkdirSync(binDir, { recursive: true });
|
|
158
158
|
const installed = {};
|
|
159
|
-
for (const name of
|
|
159
|
+
for (const name of localPathToolNames) {
|
|
160
160
|
const source = await promptExecutable(runtimeDir, name, options);
|
|
161
161
|
const target = path.join(binDir, binaryName(name));
|
|
162
162
|
if (path.resolve(source) !== path.resolve(target)) {
|
|
@@ -225,7 +225,7 @@ async function writeCasCredentials(runtimeDir, options = {}) {
|
|
|
225
225
|
return dir;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
-
async function configureTokens(runtimeDir, casDir) {
|
|
228
|
+
export async function configureTokens(runtimeDir, casDir) {
|
|
229
229
|
const env = { QDM_CAS_CONFIG_DIR: casDir };
|
|
230
230
|
const bin = (name) => path.join(runtimeDir, "bin", binaryName(name));
|
|
231
231
|
const cmrToken = (await run(bin("cas-cli"), ["token", "--app", "cmr"], { cwd: runtimeDir, env })).stdout.trim();
|
|
@@ -234,8 +234,12 @@ async function configureTokens(runtimeDir, casDir) {
|
|
|
234
234
|
const indicatorsToken = (await run(bin("cas-cli"), ["token", "--app", "indicators"], { cwd: runtimeDir, env })).stdout.trim();
|
|
235
235
|
await run(bin("qdm-indicators-cli"), ["config", "set-token", indicatorsToken], { cwd: runtimeDir, env });
|
|
236
236
|
ok("Indicators Token 已配置");
|
|
237
|
+
const sqlToken = (await run(bin("cas-cli"), ["token", "--app", "rtp"], { cwd: runtimeDir, env })).stdout.trim();
|
|
238
|
+
await run(bin("qdm-sql-cli"), ["config", "set-token", sqlToken], { cwd: runtimeDir, env });
|
|
239
|
+
ok("SQL Token 已配置");
|
|
237
240
|
await run(bin("qdm-cmr-cli"), ["config", "check-token"], { cwd: runtimeDir, env });
|
|
238
241
|
await run(bin("qdm-indicators-cli"), ["config", "check-token"], { cwd: runtimeDir, env });
|
|
242
|
+
await run(bin("qdm-sql-cli"), ["config", "check-token"], { cwd: runtimeDir, env });
|
|
239
243
|
}
|
|
240
244
|
|
|
241
245
|
export async function buildAndCheck(runtimeDir, options = {}) {
|
|
@@ -264,11 +268,12 @@ export function printDoctorSummary(doctor, options = {}) {
|
|
|
264
268
|
ok("wikis/reports");
|
|
265
269
|
ok("wikis/dims");
|
|
266
270
|
ok("wikis/rules");
|
|
267
|
-
ok("
|
|
271
|
+
ok("5 个 CLI");
|
|
268
272
|
ok("本地配置");
|
|
269
273
|
ok("CAS 凭证");
|
|
270
274
|
ok("CMR Token");
|
|
271
275
|
ok("Indicators Token");
|
|
276
|
+
ok("SQL Token");
|
|
272
277
|
if (!warnings.some((check) => check.name.startsWith("Agent hook"))) ok("Agent Hook");
|
|
273
278
|
for (const check of warnings) warn(`${check.name}${check.detail ? ` (${check.detail})` : ""}`);
|
|
274
279
|
return;
|
|
@@ -344,7 +349,8 @@ export async function installCommand(options = {}) {
|
|
|
344
349
|
blank();
|
|
345
350
|
|
|
346
351
|
step(8, 8, "配置 Agent Hook");
|
|
347
|
-
const
|
|
352
|
+
const selectedAgent = await chooseAgent(options);
|
|
353
|
+
const linkedAgents = linkAgents(runtimeDir, selectedAgent);
|
|
348
354
|
for (const [source, target] of linkedAgents) {
|
|
349
355
|
if (fs.existsSync(path.join(runtimeDir, target))) ok(`${target} -> ${source}`);
|
|
350
356
|
}
|
|
@@ -363,6 +369,7 @@ export async function installCommand(options = {}) {
|
|
|
363
369
|
tools: manifest.installedTools || {},
|
|
364
370
|
manifestSha256: manifestDigest(manifest),
|
|
365
371
|
packageVersion: packageVersion(),
|
|
372
|
+
agent: selectedAgent,
|
|
366
373
|
lastCheckAt: new Date().toISOString()
|
|
367
374
|
});
|
|
368
375
|
console.log(`安装完成:${runtimeDir}`);
|
package/src/commands/update.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import { confirm } from "../lib/prompt.js";
|
|
3
|
+
import { chooseAgent, confirm } from "../lib/prompt.js";
|
|
4
4
|
import { findWorkspaceDir, readUserState, writeState } from "../lib/paths.js";
|
|
5
5
|
import { run } from "../lib/exec.js";
|
|
6
6
|
import { installToolsFromManifest, manifestDigest, readManifest } from "../lib/manifest.js";
|
|
@@ -11,7 +11,7 @@ import { resolveLatestTool } from "../lib/tool-release.js";
|
|
|
11
11
|
import { protocolFromUrl, runGitWithProtocol } from "../lib/git-auth.js";
|
|
12
12
|
import { buildAndCheck, installRuntimeBundle, printDoctorSummary } from "./install.js";
|
|
13
13
|
import { collectDoctor } from "./doctor.js";
|
|
14
|
-
import { writeLocalConfig } from "../lib/config.js";
|
|
14
|
+
import { hasAnyAgentHook, linkAgents, writeLocalConfig } from "../lib/config.js";
|
|
15
15
|
import { action, blank, header, ok, shortSha, skip, step, warn } from "../lib/log.js";
|
|
16
16
|
|
|
17
17
|
export function isNonBlockingUpdateDoctorCheck(check) {
|
|
@@ -19,7 +19,8 @@ export function isNonBlockingUpdateDoctorCheck(check) {
|
|
|
19
19
|
check.name.startsWith("Agent hook .") ||
|
|
20
20
|
check.name === "CAS credentials file" ||
|
|
21
21
|
check.name === "CMR token" ||
|
|
22
|
-
check.name === "Indicators token"
|
|
22
|
+
check.name === "Indicators token" ||
|
|
23
|
+
check.name === "SQL token";
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
async function npmLatest() {
|
|
@@ -52,7 +53,7 @@ async function maybeUpdateTool(runtimeDir, manifest, tool, options, state) {
|
|
|
52
53
|
return null;
|
|
53
54
|
}
|
|
54
55
|
action(`发现更新:${tool.name} ${current.version || "unknown"} -> ${tag}`);
|
|
55
|
-
if (!(await confirm(`是否更新 ${tool.name}
|
|
56
|
+
if (!(await confirm(`是否更新 ${tool.name}?`))) {
|
|
56
57
|
skip(tool.name);
|
|
57
58
|
options.skippedUpdates?.push(`${tool.name} ${tag}`);
|
|
58
59
|
return null;
|
|
@@ -92,7 +93,7 @@ export async function updateWikis(runtimeDir, options, state) {
|
|
|
92
93
|
return null;
|
|
93
94
|
}
|
|
94
95
|
action(`发现更新:harness-data-wikis ${shortSha(local)} -> ${shortSha(remote)}`);
|
|
95
|
-
if (!(await confirm("是否更新 harness-data-wikis?"
|
|
96
|
+
if (!(await confirm("是否更新 harness-data-wikis?"))) {
|
|
96
97
|
skip("harness-data-wikis");
|
|
97
98
|
options.skippedUpdates?.push(`harness-data-wikis ${shortSha(remote)}`);
|
|
98
99
|
return null;
|
|
@@ -103,6 +104,20 @@ export async function updateWikis(runtimeDir, options, state) {
|
|
|
103
104
|
return { commit };
|
|
104
105
|
}
|
|
105
106
|
|
|
107
|
+
export async function restoreAgentHooksIfMissing(runtimeDir, options = {}) {
|
|
108
|
+
if (hasAnyAgentHook(runtimeDir)) {
|
|
109
|
+
ok("Agent Hook 已配置");
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
action("未检测到 Agent Hook,重新配置");
|
|
113
|
+
const agent = await chooseAgent(options);
|
|
114
|
+
const linkedAgents = linkAgents(runtimeDir, agent);
|
|
115
|
+
for (const [source, target] of linkedAgents) {
|
|
116
|
+
if (fs.existsSync(path.join(runtimeDir, target))) ok(`${target} -> ${source}`);
|
|
117
|
+
}
|
|
118
|
+
return { agent, linkedAgents };
|
|
119
|
+
}
|
|
120
|
+
|
|
106
121
|
export async function checkUpdates(workspace, options = {}) {
|
|
107
122
|
const state = readUserState();
|
|
108
123
|
const latestInstaller = await npmLatest();
|
|
@@ -131,7 +146,7 @@ export async function updateCommand(options = {}) {
|
|
|
131
146
|
const skipped = [];
|
|
132
147
|
const trackingOptions = { ...options, skippedUpdates: skipped };
|
|
133
148
|
|
|
134
|
-
step(1,
|
|
149
|
+
step(1, 7, "检查 installer");
|
|
135
150
|
const latestInstaller = await npmLatest();
|
|
136
151
|
if (latestInstaller && latestInstaller !== packageVersion()) {
|
|
137
152
|
warn(`installer 有新版本 ${packageVersion()} -> ${latestInstaller}`);
|
|
@@ -141,11 +156,11 @@ export async function updateCommand(options = {}) {
|
|
|
141
156
|
}
|
|
142
157
|
blank();
|
|
143
158
|
|
|
144
|
-
step(2,
|
|
159
|
+
step(2, 7, "检查 runtime bundle");
|
|
145
160
|
const runtimeRelease = await latestRelease("lumi-ai-lab/harness-data", options);
|
|
146
161
|
if (state.runtimeTag && state.runtimeTag !== runtimeRelease.tag_name) {
|
|
147
162
|
action(`发现更新:runtime bundle ${state.runtimeTag} -> ${runtimeRelease.tag_name}`);
|
|
148
|
-
if (await confirm("是否更新 runtime bundle?"
|
|
163
|
+
if (await confirm("是否更新 runtime bundle?")) {
|
|
149
164
|
const bundle = await installRuntimeBundle(runtimeDir, { ...trackingOptions, force: true });
|
|
150
165
|
runtimeTag = bundle.tag || runtimeRelease.tag_name;
|
|
151
166
|
changed = true;
|
|
@@ -159,7 +174,7 @@ export async function updateCommand(options = {}) {
|
|
|
159
174
|
}
|
|
160
175
|
blank();
|
|
161
176
|
|
|
162
|
-
step(3,
|
|
177
|
+
step(3, 7, "检查 CLI 工具");
|
|
163
178
|
for (const tool of manifest.tools || []) {
|
|
164
179
|
if (state.installMode === "local-path" && tool.name !== "data-harness-cli") {
|
|
165
180
|
skip(`${tool.name} 为本地路径模式,请手动检查`);
|
|
@@ -174,7 +189,7 @@ export async function updateCommand(options = {}) {
|
|
|
174
189
|
}
|
|
175
190
|
blank();
|
|
176
191
|
|
|
177
|
-
step(4,
|
|
192
|
+
step(4, 7, "检查 Wikis 知识库");
|
|
178
193
|
const wikis = await updateWikis(runtimeDir, trackingOptions, state);
|
|
179
194
|
if (wikis) {
|
|
180
195
|
changed = true;
|
|
@@ -182,7 +197,11 @@ export async function updateCommand(options = {}) {
|
|
|
182
197
|
}
|
|
183
198
|
blank();
|
|
184
199
|
|
|
185
|
-
step(5,
|
|
200
|
+
step(5, 7, "检查 Agent Hook");
|
|
201
|
+
const restoredAgent = await restoreAgentHooksIfMissing(runtimeDir, trackingOptions);
|
|
202
|
+
blank();
|
|
203
|
+
|
|
204
|
+
step(6, 7, "构建 Wikis 索引");
|
|
186
205
|
if (changed) {
|
|
187
206
|
await buildAndCheck(runtimeDir, trackingOptions);
|
|
188
207
|
} else {
|
|
@@ -190,7 +209,7 @@ export async function updateCommand(options = {}) {
|
|
|
190
209
|
}
|
|
191
210
|
blank();
|
|
192
211
|
|
|
193
|
-
step(
|
|
212
|
+
step(7, 7, "安装校验");
|
|
194
213
|
if (changed) {
|
|
195
214
|
writeLocalConfig(runtimeDir, { overwrite: true });
|
|
196
215
|
ok("本地配置已刷新");
|
|
@@ -202,6 +221,7 @@ export async function updateCommand(options = {}) {
|
|
|
202
221
|
runtimeTag,
|
|
203
222
|
tools: nextTools,
|
|
204
223
|
manifestSha256: manifestDigest(manifest),
|
|
224
|
+
...(restoredAgent ? { agent: restoredAgent.agent } : {}),
|
|
205
225
|
lastCheckAt: new Date().toISOString()
|
|
206
226
|
});
|
|
207
227
|
blank();
|
|
@@ -216,6 +236,24 @@ export async function updateCommand(options = {}) {
|
|
|
216
236
|
console.log("已跳过:");
|
|
217
237
|
for (const item of skipped) console.log(`- ${item}`);
|
|
218
238
|
}
|
|
239
|
+
if (restoredAgent) {
|
|
240
|
+
console.log("");
|
|
241
|
+
console.log("已恢复:");
|
|
242
|
+
console.log(`- Agent Hook ${restoredAgent.agent}`);
|
|
243
|
+
}
|
|
244
|
+
} else if (restoredAgent) {
|
|
245
|
+
ok("Agent Hook 已恢复");
|
|
246
|
+
writeState(runtimeDir, { ...state, agent: restoredAgent.agent, lastCheckAt: new Date().toISOString() });
|
|
247
|
+
blank();
|
|
248
|
+
console.log(`配置已恢复:${runtimeDir}`);
|
|
249
|
+
console.log("");
|
|
250
|
+
console.log("已恢复:");
|
|
251
|
+
console.log(`- Agent Hook ${restoredAgent.agent}`);
|
|
252
|
+
if (skipped.length) {
|
|
253
|
+
console.log("");
|
|
254
|
+
console.log("已跳过:");
|
|
255
|
+
for (const item of skipped) console.log(`- ${item}`);
|
|
256
|
+
}
|
|
219
257
|
} else {
|
|
220
258
|
skip("没有组件更新");
|
|
221
259
|
writeState(runtimeDir, { ...state, lastCheckAt: new Date().toISOString() });
|
package/src/lib/config.js
CHANGED
|
@@ -20,6 +20,12 @@ export const agentLinks = {
|
|
|
20
20
|
],
|
|
21
21
|
};
|
|
22
22
|
export const agentChoiceText = agentChoices.join(", ");
|
|
23
|
+
export const qdmCliBinaries = ["data-harness-cli", "qdm-cmr-cli", "qdm-indicators-cli", "qdm-sql-cli", "cas-cli"];
|
|
24
|
+
export const localPathToolNames = ["cas-cli", "qdm-indicators-cli", "qdm-cmr-cli", "qdm-sql-cli"];
|
|
25
|
+
|
|
26
|
+
export function hasAnyAgentHook(workspace) {
|
|
27
|
+
return concreteAgentNames.some((name) => fs.existsSync(path.join(workspace, `.${name}`)));
|
|
28
|
+
}
|
|
23
29
|
|
|
24
30
|
export function writeLocalConfig(workspace, options = {}) {
|
|
25
31
|
const configDir = path.join(workspace, "config");
|
|
@@ -31,8 +37,8 @@ export function writeLocalConfig(workspace, options = {}) {
|
|
|
31
37
|
}
|
|
32
38
|
const bin = (name) => path.join(workspace, "bin", binaryName(name)).replaceAll("\\", "/");
|
|
33
39
|
const casConfigDir = path.join(workspace, ".qdm-auth", "cas").replaceAll("\\", "/");
|
|
34
|
-
fs.writeFileSync(harness, `paths:\n knowledge: wikis\n\ncli:\n qdm_cmr_cli: ${bin("qdm-cmr-cli")}\n qdm_indicators_cli: ${bin("qdm-indicators-cli")}\n qdm_cas_cli: ${bin("cas-cli")}\n`);
|
|
35
|
-
fs.writeFileSync(env, `export QDM_CMR_CLI="${bin("qdm-cmr-cli")}"\nexport QDM_INDICATORS_CLI="${bin("qdm-indicators-cli")}"\nexport QDM_CAS_CLI="${bin("cas-cli")}"\nexport QDM_CAS_CONFIG_DIR="${casConfigDir}"\n`);
|
|
40
|
+
fs.writeFileSync(harness, `paths:\n knowledge: wikis\n\ncli:\n qdm_cmr_cli: ${bin("qdm-cmr-cli")}\n qdm_indicators_cli: ${bin("qdm-indicators-cli")}\n qdm_sql_cli: ${bin("qdm-sql-cli")}\n qdm_cas_cli: ${bin("cas-cli")}\n`);
|
|
41
|
+
fs.writeFileSync(env, `export QDM_CMR_CLI="${bin("qdm-cmr-cli")}"\nexport QDM_INDICATORS_CLI="${bin("qdm-indicators-cli")}"\nexport QDM_SQL_CLI="${bin("qdm-sql-cli")}"\nexport QDM_CAS_CLI="${bin("cas-cli")}"\nexport QDM_CAS_CONFIG_DIR="${casConfigDir}"\n`);
|
|
36
42
|
}
|
|
37
43
|
|
|
38
44
|
export function validateCasConfigDir(dir) {
|