@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumi-ai-lab/harness-data",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "description": "Installer and updater for Harness Data",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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", "config"]) replaceRuntimePath(runtimeDir, name, stagedRoot, backups);
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 linkedAgents = linkAgents(runtimeDir, await chooseAgent(options));
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}`);
@@ -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" || check.name.startsWith("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}?`, { defaultNo: true }))) {
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?", { defaultNo: true }))) {
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, 6, "检查 installer");
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, 6, "检查 runtime bundle");
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?", { defaultNo: true })) {
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, 6, "检查 CLI 工具");
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, 6, "检查 Wikis 知识库");
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, 6, "构建 Wikis 索引");
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(6, 6, "安装校验");
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 });