@lumi-ai-lab/harness-data 0.0.19 → 0.0.21
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/package.json +1 -1
- package/src/commands/install.js +20 -2
- package/src/commands/update.js +55 -11
- package/src/lib/config.js +4 -0
package/package.json
CHANGED
package/src/commands/install.js
CHANGED
|
@@ -57,6 +57,21 @@ function cleanupRuntimeBackups(backups) {
|
|
|
57
57
|
for (const item of backups) fs.rmSync(item.backup, { recursive: true, force: true });
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
function mergeRuntimeConfig(runtimeDir, sourceDir) {
|
|
61
|
+
const targetDir = path.join(runtimeDir, "config");
|
|
62
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
63
|
+
for (const file of fs.readdirSync(sourceDir)) {
|
|
64
|
+
const source = path.join(sourceDir, file);
|
|
65
|
+
const target = path.join(targetDir, file);
|
|
66
|
+
const stat = fs.statSync(source);
|
|
67
|
+
if (stat.isDirectory()) {
|
|
68
|
+
if (!fs.existsSync(target)) fs.cpSync(source, target, { recursive: true });
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (file.endsWith(".example") || !fs.existsSync(target)) fs.copyFileSync(source, target);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
60
75
|
export async function installRuntimeBundle(runtimeDir, options = {}) {
|
|
61
76
|
if (!options.force && fs.existsSync(path.join(runtimeDir, "agents")) &&
|
|
62
77
|
fs.existsSync(path.join(runtimeDir, "config")) &&
|
|
@@ -102,7 +117,8 @@ export async function installRuntimeBundle(runtimeDir, options = {}) {
|
|
|
102
117
|
fs.mkdirSync(path.join(stagedRoot, "config"), { recursive: true });
|
|
103
118
|
for (const file of fs.readdirSync(configSource)) fs.copyFileSync(path.join(configSource, file), path.join(stagedRoot, "config", file));
|
|
104
119
|
|
|
105
|
-
for (const name of ["agents", "bootstrap"
|
|
120
|
+
for (const name of ["agents", "bootstrap"]) replaceRuntimePath(runtimeDir, name, stagedRoot, backups);
|
|
121
|
+
mergeRuntimeConfig(runtimeDir, path.join(stagedRoot, "config"));
|
|
106
122
|
cleanupRuntimeBackups(backups);
|
|
107
123
|
} catch (error) {
|
|
108
124
|
restoreRuntimeBackups(backups);
|
|
@@ -328,7 +344,8 @@ export async function installCommand(options = {}) {
|
|
|
328
344
|
blank();
|
|
329
345
|
|
|
330
346
|
step(8, 8, "配置 Agent Hook");
|
|
331
|
-
const
|
|
347
|
+
const selectedAgent = await chooseAgent(options);
|
|
348
|
+
const linkedAgents = linkAgents(runtimeDir, selectedAgent);
|
|
332
349
|
for (const [source, target] of linkedAgents) {
|
|
333
350
|
if (fs.existsSync(path.join(runtimeDir, target))) ok(`${target} -> ${source}`);
|
|
334
351
|
}
|
|
@@ -347,6 +364,7 @@ export async function installCommand(options = {}) {
|
|
|
347
364
|
tools: manifest.installedTools || {},
|
|
348
365
|
manifestSha256: manifestDigest(manifest),
|
|
349
366
|
packageVersion: packageVersion(),
|
|
367
|
+
agent: selectedAgent,
|
|
350
368
|
lastCheckAt: new Date().toISOString()
|
|
351
369
|
});
|
|
352
370
|
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,10 +11,15 @@ 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 { hasAnyAgentHook, linkAgents, writeLocalConfig } from "../lib/config.js";
|
|
14
15
|
import { action, blank, header, ok, shortSha, skip, step, warn } from "../lib/log.js";
|
|
15
16
|
|
|
16
17
|
export function isNonBlockingUpdateDoctorCheck(check) {
|
|
17
|
-
return check.name === "Agent hook" ||
|
|
18
|
+
return check.name === "Agent hook" ||
|
|
19
|
+
check.name.startsWith("Agent hook .") ||
|
|
20
|
+
check.name === "CAS credentials file" ||
|
|
21
|
+
check.name === "CMR token" ||
|
|
22
|
+
check.name === "Indicators token";
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
async function npmLatest() {
|
|
@@ -47,7 +52,7 @@ async function maybeUpdateTool(runtimeDir, manifest, tool, options, state) {
|
|
|
47
52
|
return null;
|
|
48
53
|
}
|
|
49
54
|
action(`发现更新:${tool.name} ${current.version || "unknown"} -> ${tag}`);
|
|
50
|
-
if (!(await confirm(`是否更新 ${tool.name}
|
|
55
|
+
if (!(await confirm(`是否更新 ${tool.name}?`))) {
|
|
51
56
|
skip(tool.name);
|
|
52
57
|
options.skippedUpdates?.push(`${tool.name} ${tag}`);
|
|
53
58
|
return null;
|
|
@@ -87,7 +92,7 @@ export async function updateWikis(runtimeDir, options, state) {
|
|
|
87
92
|
return null;
|
|
88
93
|
}
|
|
89
94
|
action(`发现更新:harness-data-wikis ${shortSha(local)} -> ${shortSha(remote)}`);
|
|
90
|
-
if (!(await confirm("是否更新 harness-data-wikis?"
|
|
95
|
+
if (!(await confirm("是否更新 harness-data-wikis?"))) {
|
|
91
96
|
skip("harness-data-wikis");
|
|
92
97
|
options.skippedUpdates?.push(`harness-data-wikis ${shortSha(remote)}`);
|
|
93
98
|
return null;
|
|
@@ -98,6 +103,20 @@ export async function updateWikis(runtimeDir, options, state) {
|
|
|
98
103
|
return { commit };
|
|
99
104
|
}
|
|
100
105
|
|
|
106
|
+
export async function restoreAgentHooksIfMissing(runtimeDir, options = {}) {
|
|
107
|
+
if (hasAnyAgentHook(runtimeDir)) {
|
|
108
|
+
ok("Agent Hook 已配置");
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
action("未检测到 Agent Hook,重新配置");
|
|
112
|
+
const agent = await chooseAgent(options);
|
|
113
|
+
const linkedAgents = linkAgents(runtimeDir, agent);
|
|
114
|
+
for (const [source, target] of linkedAgents) {
|
|
115
|
+
if (fs.existsSync(path.join(runtimeDir, target))) ok(`${target} -> ${source}`);
|
|
116
|
+
}
|
|
117
|
+
return { agent, linkedAgents };
|
|
118
|
+
}
|
|
119
|
+
|
|
101
120
|
export async function checkUpdates(workspace, options = {}) {
|
|
102
121
|
const state = readUserState();
|
|
103
122
|
const latestInstaller = await npmLatest();
|
|
@@ -126,7 +145,7 @@ export async function updateCommand(options = {}) {
|
|
|
126
145
|
const skipped = [];
|
|
127
146
|
const trackingOptions = { ...options, skippedUpdates: skipped };
|
|
128
147
|
|
|
129
|
-
step(1,
|
|
148
|
+
step(1, 7, "检查 installer");
|
|
130
149
|
const latestInstaller = await npmLatest();
|
|
131
150
|
if (latestInstaller && latestInstaller !== packageVersion()) {
|
|
132
151
|
warn(`installer 有新版本 ${packageVersion()} -> ${latestInstaller}`);
|
|
@@ -136,11 +155,11 @@ export async function updateCommand(options = {}) {
|
|
|
136
155
|
}
|
|
137
156
|
blank();
|
|
138
157
|
|
|
139
|
-
step(2,
|
|
158
|
+
step(2, 7, "检查 runtime bundle");
|
|
140
159
|
const runtimeRelease = await latestRelease("lumi-ai-lab/harness-data", options);
|
|
141
160
|
if (state.runtimeTag && state.runtimeTag !== runtimeRelease.tag_name) {
|
|
142
161
|
action(`发现更新:runtime bundle ${state.runtimeTag} -> ${runtimeRelease.tag_name}`);
|
|
143
|
-
if (await confirm("是否更新 runtime bundle?"
|
|
162
|
+
if (await confirm("是否更新 runtime bundle?")) {
|
|
144
163
|
const bundle = await installRuntimeBundle(runtimeDir, { ...trackingOptions, force: true });
|
|
145
164
|
runtimeTag = bundle.tag || runtimeRelease.tag_name;
|
|
146
165
|
changed = true;
|
|
@@ -154,7 +173,7 @@ export async function updateCommand(options = {}) {
|
|
|
154
173
|
}
|
|
155
174
|
blank();
|
|
156
175
|
|
|
157
|
-
step(3,
|
|
176
|
+
step(3, 7, "检查 CLI 工具");
|
|
158
177
|
for (const tool of manifest.tools || []) {
|
|
159
178
|
if (state.installMode === "local-path" && tool.name !== "data-harness-cli") {
|
|
160
179
|
skip(`${tool.name} 为本地路径模式,请手动检查`);
|
|
@@ -169,7 +188,7 @@ export async function updateCommand(options = {}) {
|
|
|
169
188
|
}
|
|
170
189
|
blank();
|
|
171
190
|
|
|
172
|
-
step(4,
|
|
191
|
+
step(4, 7, "检查 Wikis 知识库");
|
|
173
192
|
const wikis = await updateWikis(runtimeDir, trackingOptions, state);
|
|
174
193
|
if (wikis) {
|
|
175
194
|
changed = true;
|
|
@@ -177,7 +196,11 @@ export async function updateCommand(options = {}) {
|
|
|
177
196
|
}
|
|
178
197
|
blank();
|
|
179
198
|
|
|
180
|
-
step(5,
|
|
199
|
+
step(5, 7, "检查 Agent Hook");
|
|
200
|
+
const restoredAgent = await restoreAgentHooksIfMissing(runtimeDir, trackingOptions);
|
|
201
|
+
blank();
|
|
202
|
+
|
|
203
|
+
step(6, 7, "构建 Wikis 索引");
|
|
181
204
|
if (changed) {
|
|
182
205
|
await buildAndCheck(runtimeDir, trackingOptions);
|
|
183
206
|
} else {
|
|
@@ -185,8 +208,10 @@ export async function updateCommand(options = {}) {
|
|
|
185
208
|
}
|
|
186
209
|
blank();
|
|
187
210
|
|
|
188
|
-
step(
|
|
211
|
+
step(7, 7, "安装校验");
|
|
189
212
|
if (changed) {
|
|
213
|
+
writeLocalConfig(runtimeDir, { overwrite: true });
|
|
214
|
+
ok("本地配置已刷新");
|
|
190
215
|
const doctor = await collectDoctor(runtimeDir, trackingOptions);
|
|
191
216
|
printDoctorSummary(doctor, { nonBlocking: isNonBlockingUpdateDoctorCheck });
|
|
192
217
|
if (doctor.checks.some((check) => !check.ok && !isNonBlockingUpdateDoctorCheck(check))) throw new Error("doctor failed; update is incomplete");
|
|
@@ -195,6 +220,7 @@ export async function updateCommand(options = {}) {
|
|
|
195
220
|
runtimeTag,
|
|
196
221
|
tools: nextTools,
|
|
197
222
|
manifestSha256: manifestDigest(manifest),
|
|
223
|
+
...(restoredAgent ? { agent: restoredAgent.agent } : {}),
|
|
198
224
|
lastCheckAt: new Date().toISOString()
|
|
199
225
|
});
|
|
200
226
|
blank();
|
|
@@ -209,6 +235,24 @@ export async function updateCommand(options = {}) {
|
|
|
209
235
|
console.log("已跳过:");
|
|
210
236
|
for (const item of skipped) console.log(`- ${item}`);
|
|
211
237
|
}
|
|
238
|
+
if (restoredAgent) {
|
|
239
|
+
console.log("");
|
|
240
|
+
console.log("已恢复:");
|
|
241
|
+
console.log(`- Agent Hook ${restoredAgent.agent}`);
|
|
242
|
+
}
|
|
243
|
+
} else if (restoredAgent) {
|
|
244
|
+
ok("Agent Hook 已恢复");
|
|
245
|
+
writeState(runtimeDir, { ...state, agent: restoredAgent.agent, lastCheckAt: new Date().toISOString() });
|
|
246
|
+
blank();
|
|
247
|
+
console.log(`配置已恢复:${runtimeDir}`);
|
|
248
|
+
console.log("");
|
|
249
|
+
console.log("已恢复:");
|
|
250
|
+
console.log(`- Agent Hook ${restoredAgent.agent}`);
|
|
251
|
+
if (skipped.length) {
|
|
252
|
+
console.log("");
|
|
253
|
+
console.log("已跳过:");
|
|
254
|
+
for (const item of skipped) console.log(`- ${item}`);
|
|
255
|
+
}
|
|
212
256
|
} else {
|
|
213
257
|
skip("没有组件更新");
|
|
214
258
|
writeState(runtimeDir, { ...state, lastCheckAt: new Date().toISOString() });
|
package/src/lib/config.js
CHANGED
|
@@ -21,6 +21,10 @@ export const agentLinks = {
|
|
|
21
21
|
};
|
|
22
22
|
export const agentChoiceText = agentChoices.join(", ");
|
|
23
23
|
|
|
24
|
+
export function hasAnyAgentHook(workspace) {
|
|
25
|
+
return concreteAgentNames.some((name) => fs.existsSync(path.join(workspace, `.${name}`)));
|
|
26
|
+
}
|
|
27
|
+
|
|
24
28
|
export function writeLocalConfig(workspace, options = {}) {
|
|
25
29
|
const configDir = path.join(workspace, "config");
|
|
26
30
|
fs.mkdirSync(configDir, { recursive: true });
|