@lumi-ai-lab/harness-data 0.0.21 → 0.0.23
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 +10 -2
- package/package.json +1 -1
- package/src/cli.js +6 -3
- package/src/commands/auth.js +34 -0
- package/src/commands/doctor.js +6 -4
- package/src/commands/install.js +17 -8
- package/src/commands/update.js +2 -1
- package/src/lib/config.js +4 -2
- package/src/lib/exec.js +3 -1
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
|
|
|
@@ -32,12 +32,20 @@ Update an existing runtime interactively:
|
|
|
32
32
|
npx @lumi-ai-lab/harness-data update
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
Reconfigure CAS credentials after an account/password change or after `.qdm-auth` was deleted:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx @lumi-ai-lab/harness-data auth --dir /path/to/runtime
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The command recreates `.qdm-auth/cas`, stores the new encrypted CAS credentials, refreshes the CMR, Indicators, and SQL tokens, and validates all three tokens.
|
|
42
|
+
|
|
35
43
|
Diagnose a runtime:
|
|
36
44
|
|
|
37
45
|
```bash
|
|
38
46
|
npx @lumi-ai-lab/harness-data doctor
|
|
39
47
|
```
|
|
40
48
|
|
|
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.
|
|
49
|
+
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
50
|
|
|
43
51
|
`--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/cli.js
CHANGED
|
@@ -2,8 +2,9 @@ import { installCommand } from "./commands/install.js";
|
|
|
2
2
|
import { updateCommand } from "./commands/update.js";
|
|
3
3
|
import { doctorCommand } from "./commands/doctor.js";
|
|
4
4
|
import { versionCommand } from "./commands/version.js";
|
|
5
|
+
import { authCommand } from "./commands/auth.js";
|
|
5
6
|
|
|
6
|
-
const commands = new Set(["install", "update", "doctor", "version"]);
|
|
7
|
+
const commands = new Set(["install", "update", "auth", "doctor", "version"]);
|
|
7
8
|
|
|
8
9
|
function parse(argv) {
|
|
9
10
|
const args = argv.slice(2);
|
|
@@ -31,17 +32,19 @@ export async function main(argv) {
|
|
|
31
32
|
const { command, options, unknown } = parse(argv);
|
|
32
33
|
if (command === "install") return installCommand(options);
|
|
33
34
|
if (command === "update") return updateCommand(options);
|
|
35
|
+
if (command === "auth") return authCommand(options);
|
|
34
36
|
if (command === "doctor") return doctorCommand(options);
|
|
35
37
|
if (command === "version") return versionCommand(options);
|
|
36
|
-
console.log(`Usage: harness-data <install|update|doctor|version> [options]
|
|
38
|
+
console.log(`Usage: harness-data <install|update|auth|doctor|version> [options]
|
|
37
39
|
|
|
38
40
|
Commands:
|
|
39
41
|
install Install a Harness Data runtime in the current directory
|
|
40
42
|
update Interactively check and apply runtime, CLI, and wikis updates
|
|
43
|
+
auth Configure CAS credentials and refresh access tokens
|
|
41
44
|
doctor Diagnose workspace CLI, config, auth, index, and Agent hooks
|
|
42
45
|
version Print installer, repository, wikis, and manifest versions
|
|
43
46
|
|
|
44
|
-
Install options:
|
|
47
|
+
Install and auth options:
|
|
45
48
|
--dir PATH Runtime directory (default: current directory)
|
|
46
49
|
--agent NAME claude, codex, pi, openclaw, hermes, both, or all
|
|
47
50
|
--github-token TOKEN GitHub token for private Release assets
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { findWorkspaceDir } from "../lib/paths.js";
|
|
4
|
+
import { packageVersion } from "../lib/package.js";
|
|
5
|
+
import { binaryName } from "../lib/platform.js";
|
|
6
|
+
import { blank, header, ok, step } from "../lib/log.js";
|
|
7
|
+
import { configureTokens, writeCasCredentials } from "./install.js";
|
|
8
|
+
|
|
9
|
+
const requiredBinaries = ["cas-cli", "qdm-cmr-cli", "qdm-indicators-cli", "qdm-sql-cli"];
|
|
10
|
+
|
|
11
|
+
function validateAuthRuntime(runtimeDir) {
|
|
12
|
+
if (!fs.existsSync(runtimeDir)) throw new Error(`runtime directory does not exist: ${runtimeDir}`);
|
|
13
|
+
for (const name of requiredBinaries) {
|
|
14
|
+
const file = path.join(runtimeDir, "bin", binaryName(name));
|
|
15
|
+
if (!fs.existsSync(file)) throw new Error(`runtime CLI is missing: ${file}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function authCommand(options = {}) {
|
|
20
|
+
const runtimeDir = findWorkspaceDir(options.dir);
|
|
21
|
+
validateAuthRuntime(runtimeDir);
|
|
22
|
+
header("Harness Data 认证配置", packageVersion(), [`运行目录:${runtimeDir}`]);
|
|
23
|
+
|
|
24
|
+
step(1, 2, "配置 CAS 凭证");
|
|
25
|
+
const casDir = await writeCasCredentials(runtimeDir, options);
|
|
26
|
+
blank();
|
|
27
|
+
|
|
28
|
+
step(2, 2, "刷新并校验访问 Token");
|
|
29
|
+
await configureTokens(runtimeDir, casDir);
|
|
30
|
+
blank();
|
|
31
|
+
|
|
32
|
+
ok("CAS 认证配置完成");
|
|
33
|
+
return { runtimeDir, casDir };
|
|
34
|
+
}
|
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)) {
|
|
@@ -213,29 +213,37 @@ function casConfigDir(runtimeDir) {
|
|
|
213
213
|
return path.join(runtimeDir, ".qdm-auth", "cas");
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
-
async function writeCasCredentials(runtimeDir, options = {}) {
|
|
216
|
+
export async function writeCasCredentials(runtimeDir, options = {}) {
|
|
217
217
|
const username = options.casUsername || await ask("CAS 用户名:", options);
|
|
218
218
|
const password = options.casPassword || await askSecret("CAS 密码:", options);
|
|
219
219
|
if (!username || !password) throw new Error("CAS username and password are required");
|
|
220
220
|
const dir = casConfigDir(runtimeDir);
|
|
221
221
|
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
222
222
|
const env = { QDM_CAS_CONFIG_DIR: dir };
|
|
223
|
-
await run(path.join(runtimeDir, "bin", binaryName("cas-cli")), ["config", "set-credentials", "--username", username, "--password", password], {
|
|
223
|
+
await run(path.join(runtimeDir, "bin", binaryName("cas-cli")), ["config", "set-credentials", "--username", username, "--password", password], {
|
|
224
|
+
cwd: runtimeDir,
|
|
225
|
+
env,
|
|
226
|
+
sensitiveArgs: [5]
|
|
227
|
+
});
|
|
224
228
|
ok("CAS 凭证已加密保存到 .qdm-auth/cas/credentials.enc");
|
|
225
229
|
return dir;
|
|
226
230
|
}
|
|
227
231
|
|
|
228
|
-
async function configureTokens(runtimeDir, casDir) {
|
|
232
|
+
export async function configureTokens(runtimeDir, casDir) {
|
|
229
233
|
const env = { QDM_CAS_CONFIG_DIR: casDir };
|
|
230
234
|
const bin = (name) => path.join(runtimeDir, "bin", binaryName(name));
|
|
231
235
|
const cmrToken = (await run(bin("cas-cli"), ["token", "--app", "cmr"], { cwd: runtimeDir, env })).stdout.trim();
|
|
232
|
-
await run(bin("qdm-cmr-cli"), ["config", "set-token", cmrToken], { cwd: runtimeDir, env });
|
|
236
|
+
await run(bin("qdm-cmr-cli"), ["config", "set-token", cmrToken], { cwd: runtimeDir, env, sensitiveArgs: [2] });
|
|
233
237
|
ok("CMR Token 已配置");
|
|
234
238
|
const indicatorsToken = (await run(bin("cas-cli"), ["token", "--app", "indicators"], { cwd: runtimeDir, env })).stdout.trim();
|
|
235
|
-
await run(bin("qdm-indicators-cli"), ["config", "set-token", indicatorsToken], { cwd: runtimeDir, env });
|
|
239
|
+
await run(bin("qdm-indicators-cli"), ["config", "set-token", indicatorsToken], { cwd: runtimeDir, env, sensitiveArgs: [2] });
|
|
236
240
|
ok("Indicators Token 已配置");
|
|
241
|
+
const sqlToken = (await run(bin("cas-cli"), ["token", "--app", "rtp"], { cwd: runtimeDir, env })).stdout.trim();
|
|
242
|
+
await run(bin("qdm-sql-cli"), ["config", "set-token", sqlToken], { cwd: runtimeDir, env, sensitiveArgs: [2] });
|
|
243
|
+
ok("SQL Token 已配置");
|
|
237
244
|
await run(bin("qdm-cmr-cli"), ["config", "check-token"], { cwd: runtimeDir, env });
|
|
238
245
|
await run(bin("qdm-indicators-cli"), ["config", "check-token"], { cwd: runtimeDir, env });
|
|
246
|
+
await run(bin("qdm-sql-cli"), ["config", "check-token"], { cwd: runtimeDir, env });
|
|
239
247
|
}
|
|
240
248
|
|
|
241
249
|
export async function buildAndCheck(runtimeDir, options = {}) {
|
|
@@ -264,11 +272,12 @@ export function printDoctorSummary(doctor, options = {}) {
|
|
|
264
272
|
ok("wikis/reports");
|
|
265
273
|
ok("wikis/dims");
|
|
266
274
|
ok("wikis/rules");
|
|
267
|
-
ok("
|
|
275
|
+
ok("5 个 CLI");
|
|
268
276
|
ok("本地配置");
|
|
269
277
|
ok("CAS 凭证");
|
|
270
278
|
ok("CMR Token");
|
|
271
279
|
ok("Indicators Token");
|
|
280
|
+
ok("SQL Token");
|
|
272
281
|
if (!warnings.some((check) => check.name.startsWith("Agent hook"))) ok("Agent Hook");
|
|
273
282
|
for (const check of warnings) warn(`${check.name}${check.detail ? ` (${check.detail})` : ""}`);
|
|
274
283
|
return;
|
package/src/commands/update.js
CHANGED
|
@@ -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() {
|
package/src/lib/config.js
CHANGED
|
@@ -20,6 +20,8 @@ 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"];
|
|
23
25
|
|
|
24
26
|
export function hasAnyAgentHook(workspace) {
|
|
25
27
|
return concreteAgentNames.some((name) => fs.existsSync(path.join(workspace, `.${name}`)));
|
|
@@ -35,8 +37,8 @@ export function writeLocalConfig(workspace, options = {}) {
|
|
|
35
37
|
}
|
|
36
38
|
const bin = (name) => path.join(workspace, "bin", binaryName(name)).replaceAll("\\", "/");
|
|
37
39
|
const casConfigDir = path.join(workspace, ".qdm-auth", "cas").replaceAll("\\", "/");
|
|
38
|
-
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`);
|
|
39
|
-
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`);
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
export function validateCasConfigDir(dir) {
|
package/src/lib/exec.js
CHANGED
|
@@ -20,7 +20,9 @@ export function run(command, args = [], options = {}) {
|
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
22
|
const detail = stderr.trim() || stdout.trim();
|
|
23
|
-
|
|
23
|
+
const sensitiveArgs = new Set(options.sensitiveArgs || []);
|
|
24
|
+
const displayArgs = args.map((arg, index) => sensitiveArgs.has(index) ? "******" : arg);
|
|
25
|
+
reject(new Error(`${command} ${displayArgs.join(" ")} failed${detail ? `: ${detail}` : ""}`));
|
|
24
26
|
});
|
|
25
27
|
});
|
|
26
28
|
}
|